How to Build a Music Streaming App with React using Auth0 and Cloudinary
Tags: react, webdev, javascript
Published: 2021-10-17 14:30:54

Since its debut, music streaming has developed dramatically, and it is currently one of the most acceptable methods to listen to music. Many streaming sites let you hear for free, which has reduced the need for piracy while ensuring that artists are compensated fairly. It's also very accessible, with numerous possibilities.

We'll learn how to use Auth0 and Cloudinary to develop a music streaming app in this tutorial.

## Sandbox

This project was completed in a [Codesandbox](https://codesandbox.io/s/stream-music-with-auth0-and-cloudinary-demo-de7pd?file=/src/index.js). To get started quickly, fork the [Codesandbox](https://codesandbox.io/s/stream-music-with-auth0-and-cloudinary-demo-de7pd?file=/src/index.js) or run the project.

GitHub Repository:

{% github https://github.com/Olanetsoft/music-app-with-auth0-and-cloudinary %}


## What is Auth0?

[Auth0](https://auth0.com/) is an extensible authentication and authorisation system that is simple to set up. It also provides a complete identity and access management system that works right out of the box, with the ability to customise, expand, and develop new features as needed.


## What is Cloudinary?

[Cloudinary](https://cloudinary.com/documentation/image_video_and_file_upload#upload_options_overview) provides a secure and comprehensive API for uploading media files fast and efficiently from the server-side, the browser, or a mobile application. We can upload media assets using Cloudinary's REST API or client libraries (SDKs). These SDKs wrap the upload API and make it easier to integrate with websites and mobile apps.


## Creating a new React project and Installing dependencies

To create a new project, we use the `npx create-react-app` command to scaffold a new project in a directory of our choice.

To install the dependencies we will be using the command below:

```
cd <project name> 

npm install @auth0/auth0-react @supabase/supabase-js bootstrap moment react-audio-player react-bootstrap react-helmet
```

Once the app is created, and the dependencies are installed, we will see a message with instructions for navigating to our site and running it locally. We do this with the command.

```
    npm start
```

React.js will start a hot-reloading development environment accessible by default at http://localhost:3000


## Setting Up Auth0 Account

Kindly visit [Auth0](https://auth0.com/signup?place=header&type=button&text=sign%20up) to sign up if you haven’t or [login](https://auth0.auth0.com/u/login/identifier?state=hKFo2SBOcFBxd2xEWm1aTTlPWkYtYm5aVVFDZFhPQjRhN3JWM6Fur3VuaXZlcnNhbC1sb2dpbqN0aWTZIGlNaVJIQlkybG9MM0VvM3otdzdpR3QwaWlzTkotX3FEo2NpZNkgYkxSOVQ1YXI2bkZ0RE80ekVyR1hkb3FNQ000aU5aU1Y) to dashboard, click the `Applications` dropdown then `application` and finally click the `Create Application` button as shown below:

![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633441865270_Screenshot+2021-10-05+at+14.47.15.png)


We can now create our application as shown below:


![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633442087839_Screenshot+2021-10-05+at+14.53.17.png)


As shown below, we have successfully created our application, but we need to set URLs to point back to our application. 


![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633442678530_Screenshot+2021-10-05+at+14.57.32.png)


Scroll down to the Application URIs section and set the following

`Allowed Callback URLs` = https://de7pd.csb.app
`Allowed Logout URLs` = https://de7pd.csb.app
`Allowed Web Origins`= https://de7pd.csb.app

Replace https://de7pd.csb.app with our application URL or [http://localhost:3000](http://localhost:3000) that we set up earlier. We will be making use of Domain and Client ID in our application later in this tutorial.

## Setting up our Application UI and Auth0 integration.

Let’s import and set up our application to use the bootstrap dependencies we installed. Navigate to `public/index.html` update the file by linking the bootstrap CSS and js with the snippet below:

- Adding CSS reference

```HTML
<!DOCTYPE html>
    <html lang="en">
      <head>
        //...
    
        <link
          rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css"
          integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU"
          crossorigin="anonymous"
        />
        <title>Music Streaming App</title>
      </head>
      <body>
        //...
      </body>
    </html>
```


- Adding JS reference
```HTML
    <!DOCTYPE html>
    <html lang="en">
      <head>
      //...
      </head>
      <body>
        <noscript>
          You need to enable JavaScript to run this app.
        </noscript>
        <div id="root"></div>
        <script
          src="https://unpkg.com/react/umd/react.production.min.js"
          crossorigin
        ></script>
        <script
          src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"
          crossorigin
        ></script>
        <script
          src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js"
          crossorigin
        ></script>
      </body>
    </html>
```

We require the user interface to stream music on the home page. We will create this by updating the `app.js` file to a component. Since it is the React tradition to work with a component structure, we will create a folder called `components` in the `src` folder and create `header.js`, `music.js`, and `musicList.js` components.

In the `components/music.js` file, let us update it with the snippet below:

```Javascript
    import ReactAudioPlayer from "react-audio-player";
    import moment from "moment";
    
    export default function Music({ musicList, index }) {
      return (
        <div className="col-md-4">
          <div className="card p-3 mb-2" key={index}>
            <div className="d-flex justify-content-between">
              <div className="d-flex flex-row align-items-center">
                <div className="icon">
                  {" "}
                  <i className="bx bxl-mailchimp"></i>{" "}
                </div>
                <div className="ms-2 c-details">
                  <h6 className="mb-0">{musicList.name}</h6>{" "}
                  <span>{moment(musicList.created_at).format("MMMM Do YYYY")}</span>
                </div>
              </div>
              <div className="badge">
                {" "}
                <span role="img" aria-label="">
                  Hot 🔥
                </span>{" "}
              </div>
            </div>
            <div className="mt-2">
              <h4 className="heading">{musicList.title}</h4>
              <div className="mt-2">
                <ReactAudioPlayer src={`${musicList.url}`} controls />
              </div>
            </div>
          </div>
        </div>
      );
    }
```
In the snippet above, we created a component for a single music card with `musicList` and `index` props. We also imported ReactAudioPlayer and moment for the audio player and upload date formatting, respectively.

Inside the `musicList.js` component, we will update it with the snippet below by importing the music component and iterating through the sample musicList array.

```Javascript
    import Music from "./music";
    
    export default function App() {
      const musicList = [
        {
          name: "olanetsoft",
          title: "Bang Bang",
          url: "https://res.cloudinary.com/demo/video/upload/dog.mp3",
          created_at:"2021-10-04T23:30:01.000Z",
        }
      ]
      return (
        <div className="row">
          {musicList.map((m, key) => (
            <Music musicList={m} index={key} />
          ))}
        </div>
      );
    }
```    

Let us update the `header.js` component we created earlier with the snippet below:

```Javascript
    import { Button } from "react-bootstrap";
    
    export default function Header() {
    
      return (
        <div className="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
          <h5 className="my-0 mr-md-auto font-weight-normal">
            Music Streaming App with Auth0 and Cloudinary
          </h5>
          <nav className="my-2 my-md-0 mr-md-3">
            <a className="p-2 text-success" href="/">
              Home
            </a>
            <a className="p-2 text-danger" href="/">
              Trending
            </a>
            <a className="p-2 text-info" href="/">
              Top Songs
            </a>
          </nav>
            <Button
              id="btnUpload"
              className="btn margin"
              variant="primary"
            >
              Upload Song
            </Button>
        </div>
      );
    } 
```

We can now update our `src/app.js` file as shown below:

```Javascript

    import MusicList from "../components/musicList";
    import "./styles.css";
    
    export default function App() {
      return (
        <div className="container mt-5 mb-3">
          <Header />
          <MusicList />
        </div>
      );
    }
```

The current user interface doesn’t look aesthetically pleasing, we’ll add some styling with CSS. We will update `src/styles.css` file with the following content in this GitHub Gist.

{% gist https://gist.github.com/Olanetsoft/96c25705f9d8e34218ffe7668d1ed3f9 %}

Our application should now look like this on http://localhost:3000/:


![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633445977116_Screenshot+2021-10-05+at+15.59.04.png)


We're currently working with sample data, which is not ideal. We should be able to upload and stream songs that others have uploaded.

We'll use Auth0 to track who's uploading new songs, and then we'll use Cloudinary to do the actual uploading before saving it to the database.


## Setting up Auth0 in our Application

Let’s create `.env` file in root of our project and populate it with Domain and Client ID from our Auth0 dashboard with the snippet below:

```Bash
    AUTH0_DOMAIN=dev-9hbpo12k.us.auth0.com
    AUTH0_CLIENT_ID=tdYpNQ8Qqjymi0dOC7wZdGGWlYCN6FR3
```

Inside `src/index.js` let us import `Auth0Provider` and setup our application with the snippet below:

```Javascript
    import { StrictMode } from "react";
    import ReactDOM from "react-dom";
    import App from "./App";
    import { Auth0Provider } from "@auth0/auth0-react";
    
    const domain = process.env.AUTH0_DOMAIN;
    const clientId = process.env.AUTH0_CLIENT_ID;
    
    const rootElement = document.getElementById("root");
    
    ReactDOM.render(
      <StrictMode>
        <Auth0Provider
          domain={domain}
          clientId={clientId}
          redirectUri={window.location.origin}
        >
          <App />
        </Auth0Provider>
        ,
      </StrictMode>,
      rootElement
    );
```

We can now create `login-button.js` , `logout-button.js` and `loading.js` component inside `components` folder respectively, using the snippet below:

Inside `components/login-button.js` 

```Javascript
    import { useAuth0 } from "@auth0/auth0-react";
    import { Button } from "react-bootstrap";
    
    export default function Login() {
      const { loginWithRedirect } = useAuth0();
      return (
        <Button
          id="btnLogin"
          className="btn margin"
          onClick={() => loginWithRedirect()}
          variant="primary"
        >
          Upload Music
        </Button>
      );
    }
```  

 `components/logout-button.js`

```Javascript
    import { useAuth0 } from "@auth0/auth0-react";
    import { Button } from "react-bootstrap";
    
    export default function Logout() {
      const { logout } = useAuth0();
      return (
        <Button
          id="btnLogin"
          className="btn margin"
          onClick={() => logout()}
          variant="danger"
        >
          Logout
        </Button>
      );
    }
```   

Then inside `components/loading.js`

```Javascript
    import "../src/styles.css";
    export default function Loading() {
      return <div className="spinner"></div>;
    }
```

We may proceed to import the `login` and  `logout` component inside `header.js` file created earlier as shown below:

```Javascript
    import { useState, useEffect } from "react";
    import { Button } from "react-bootstrap";
    
    import { useAuth0 } from "@auth0/auth0-react";
    import Login from "../components/login-button";
    import Logout from "../components/logout-button";
    
    export default function Header() {
      const { isAuthenticated } = useAuth0();
      
      return (
        <div className="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
             {/*  */}
          {isAuthenticated ? (
            <>
              <div>
                <Button
                  id="btnUpload"
                  className="btn margin"
                  variant="primary"
                >
                  Upload Song
                </Button>
                &nbsp;&nbsp;
                <Logout />
              </div>
            </>
          ) : (
            <Login />
          )}
        </div>
      );
    }
```  

Updating `src/app.js` 

```Javascript
    //...
    import Loading from "../components/loading";
    
    export default function App() {
      const { isLoading } = useAuth0();
      if (isLoading) {
        return <Loading />;
      }
      return (
        //....
      );
    }
 ```

Let us test our application, and we should have something similar to what we have below after clicking the `Upload Song` button.

![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633447867923_Screenshot+2021-10-05+at+16.27.49.png)



![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633447877517_Screenshot+2021-10-05+at+16.30.03.png)


In the screenshot above, we've successfully logged in, and you'll notice that the UI in the header has changed to include a logout button.


## Configuring Cloudinary and DB to Upload Songs

We will be using Cloudinary’s upload widget because of its ability lets us upload media assets from multiple sources including Dropbox, Facebook, Instagram,.


> [C](https://cloudinary.com/users/register/free)[reate a free cloudinary account](https://cloudinary.com/users/register/free) to obtain your cloud name and upload_preset. 

Upload presets allow us to centrally define a set of asset upload choices rather than providing them in each upload call. A Cloudinary cloud name is a unique identifier associated with our Cloudinary account.

First, from a content delivery network (CDN) we will include the Cloudinary widget’s JavaScript file in our index.js located in `src/app.``js`. We include this file using the [react-helmet](https://www.npmjs.com/package/react-helmet)’s `<Helmet>` component, which lets us add data to the Head portion of our HTML document in React.

```Javascript
    //..
    
    import "./styles.css";
    
    import { Helmet } from "react-helmet";
    
    export default function App() {
      //...
      return (
        <div className="container mt-5 mb-3">
          <Helmet>
            <meta charSet="utf-8" />
            <script
              src="https://widget.Cloudinary.com/v2.0/global/all.js"
              type="text/javascript"
            ></script>
        //...
        </div>
      );
    }
```

The widget requires our Cloudinary `cloud_name` and `uploadPreset`. The `createWidget()` function creates a new upload widget and on the successful upload of either a video or audio, we assign the public_id of the asset to the relevant state variable.

To get our `cloudname` and `uploadPreset` we follow the steps below:

The cloud name is obtained from our Cloudinary dashboard as shown below.

![cloudinary demo](https://paper-attachments.dropbox.com/s_B1FCD2502725CB3D5CEC87B28CE445B6BC2160404292066EB7F16F68C17039B2_1622826668451_Screenshot+2021-06-04+at+18.09.43.png)


An upload preset can be found in the “Upload” tab of our Cloudinary settings page, which we access by clicking on the gear icon in the top right corner of the dashboard page.

![cloudinary demo](https://paper-attachments.dropbox.com/s_B1FCD2502725CB3D5CEC87B28CE445B6BC2160404292066EB7F16F68C17039B2_1622826303905_Screenshot+2021-06-04+at+18.03.09.png)


We then click on the `Upload` tab on the settings page:

![cloudinary demo](https://paper-attachments.dropbox.com/s_B1FCD2502725CB3D5CEC87B28CE445B6BC2160404292066EB7F16F68C17039B2_1622826506106_Screenshot+2021-06-04+at+18.07.09.png)


We scroll down to the bottom of the page to the upload presets section, where we see our upload preset or the option to create one if we don't have any.

Let us update our `components/header.js` with the snippet below:

{% gist https://gist.github.com/Olanetsoft/c0dedcdc8c5186ee35ae08f44d11f37a %}


Let us open our app in the browser and click the `Upload Song` button; we should see something like this:

![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633448929914_Screenshot+2021-10-05+at+16.48.27.png)


We can further customise the widget with more information in this [documentation](https://cloudinary.com/documentation/upload_widget#look_and_feel_customization).

We have successfully configured and setup cloudinary in our application, but we will also integrate a [supabase](https://supabase.io/) database to save all of the songs that users have uploaded.

let us create `client.js` to integrate supabase with the sippet below:

```Javascript
    import { createClient } from "@supabase/supabase-js";
    
    const URL = "https://kpriwlucrliyacagwjvk.supabase.co";
    const ANNON_PUBLIC_SECRET = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzMzM2NzU2OSwiZXhwIjoxOTQ4OTQzNgY5fQ.uBBXtyxbwKixUgql4tiYUsqOgSPyB4mLSc2kybqPCPI";
    
    export const supabase = createClient(
      URL,
      ANNON_PUBLIC_SECRET
    );
```

To get the `url` and `annon public` key, create a [supabase](https://supabase.io/) account, start a new project, go to `settings`  then `Api` tab.

We'll create a new table named `songs` with columns for `url`, `name`, and `title` by going to the table editor tab on the sidebar. Let’s ensure the column type is text for all the columns created.

After successfully creating our table, let's update the `components/header.js` file with the snippet below:


{% gist https://gist.github.com/Olanetsoft/c216ae1b80e1639d1bac746910f0b663 %}

In the preceding code line,

- We created state variables that are updated when the upload is completed.
- We created a function called `createSong` that connects to the songs table in `Supabase`, and then we input our data. 
- We then verify the variables to ensure they aren't undefined before using the `createPost` method to save them in the database.


Let's update the musicList component to retrieve all uploaded songs with the snippet shown below:

```Javascript
    import { useState, useEffect } from "react";
    import { supabase } from "../client";
    import Music from "./music";
    
    export default function App() {
      const [musicList, setMusicList] = useState([]);
    
      useEffect(() => {
        fetchSongs();
      }, []);
    
      async function fetchSongs() {
        const { data } = await supabase.from("songs").select();
    
        setMusicList(data);
      }
    
      return (
        <div className="row">
          {musicList.map((m, key) => (
            <Music musicList={m} index={key} />
          ))}
        </div>
      );
    }
```
Voila 🥳 We are all set; we can now successfully upload songs, stream songs, etc.


![cloudinary demo](https://paper-attachments.dropbox.com/s_5A59389CEFCA2DAA2AEA7018F41C7F13F6E089A1E1E791707FD1B10D16131073_1633450883689_Screenshot+2021-10-05+at+17.20.57.png)

## Conclusion

This article explains how to use Auth0 and Cloudinary to build a music streaming app utilising Cloudinary's widget capability.


## Resources

- [Media](https://cloudinary.com/documentation/image_video_and_file_upload) [upload on Cloudinary](https://cloudinary.com/documentation/image_video_and_file_upload)
- [React-Helmet](https://www.npmjs.com/package/react-helmet)
- [Auth0](https://www.npmjs.com/package/@auth0/auth0-react)

Content created for the [Hackmamba](https://content.hackmamba.io/) Jamstack Content Hackathon using [Auth0](https://auth0.com/) and [Cloudinary](https://cloudinary.com/)