Create a personalized link generator with Cloudinary + Auth0

Demola Malomo - Oct 17 '21 - - Dev Community

Shared Links or Link sharing are specially created link to conveniently share large files that you wouldn’t be able to send via email or mobile devices. They serve as mediums that point to your files or contents stored in the cloud.

In this post, we will discuss how to build a personalized and secure link sharing application using Cloudinary, Auth0, and Next.js. At the end of this tutorial, we would have learnt how to upload files to Cloudinary, get sharable links, share using a device clipboard, and secure the web application using Auth0.

By way of introduction, Cloudinary is a platform on which you can quickly and easily upload, store, manage, transform, and deliver images and videos for websites and apps. The platform also offers a vast collection of SDKs for frontend frameworks and libraries.

Auth0 is a platform that offers systems to rapidly integrate authentication and authorization for web, mobile, and legacy applications.

Next.js is a React-based front-end development framework that enables functionalities like server-side rendering, static site generation, file-system routing (with no need to manually configure react-router-dom), and API endpoints for backend features.

Source code

You can find the source code of this project here.

Prerequisites

The next steps in this post require JavaScript and React.js experience. Experience with Next.js isn’t a requirement, but it’s nice to have.

We also need the following:

We need to create a Next.js starter project by navigating to the desired directory and running the command below in our terminal

npx create-next-app link_generator && cd link_generator
Enter fullscreen mode Exit fullscreen mode

This command creates a Next.js project called link_generator and navigates into the project directory.

We proceed to install the required dependencies with:

npm install react-helmet @auth0/nextjs-auth0
Enter fullscreen mode Exit fullscreen mode

react-helmet is a library that takes plain HTML tags and outputs plain HTML tags. It will help us avoid the Reference error: Window is not Defined error when using client-side only packages in Next.js.

@auth0/nextjs-auth0 is a library for implementing user authentication in Next.js applications.

File upload and link sharing with Cloudinary

With the project dependencies installed, we can leverage Next.js CSS Module support to style our page by replacing the content in Home.module.css in the styles folder with the gist below:

Home.module.css

With that done, we can update index.js file in the pages folder by importing the styles created and configure Cloudinary Upload Widget to upload files to Cloudinary. We also include states to handle link generation and copying.

    import { useState } from 'react';
    import { Helmet } from 'react-helmet';
    import styles from '../styles/Home.module.css';

    export default function Home() {
      const [url, setURL] = useState(null);
      const [copy, setCopy] = useState('Copy File');

      const openWidget = () => {
        setURL(null);
        window.cloudinary
          .createUploadWidget(
            {
              cloudName: 'dtgbzmpca',
              uploadPreset: 'tca2j0ee',
            },
            (error, result) => {
              if (!error && result && result.event === 'success') {
                setURL(result.info.url);
              }
            }
          )
          .open();
      };

      return (
        <div>
          <Helmet>
            <script src='https://upload-widget.cloudinary.com/global/all.js' />
          </Helmet>
          <main className={styles.files}>
            {/* remaining JSX comes here */}
          </main>
        </div>
      );
    }
Enter fullscreen mode Exit fullscreen mode

The Cloudinary Upload Widget also configures cloudName, uploadPreset, use the callback function to check for error, and update the URL based on the returned result. The cloudname is the name of our Cloudinary account while uploadPreset is a parameter that enables us to centrally define a set of asset upload options instead of specifying them in each upload call. Finally, we used react-helmet to add Cloudinary Upload Widget CDN to the application.

Shaded portions are where your cloud name is located in Cloudinary

To create a new upload preset, click on the Settings Icon, select the Upload tab, navigate to the Upload presets section, click on the Add Upload preset.

select preset

copy preset name, change to Unsigned and save

Find out more about Cloudinary Upload Widget here.

Next, we need to include our markup for upload and conditionally display returned link.

    //imports here

    export default function Home() {
      //states here

      const openWidget = () => {
        //upload widget code
      };

      return (
        <div>
          <Helmet>
            <script src='https://upload-widget.cloudinary.com/global/all.js' />
          </Helmet>
          <main className={styles.files}>
            <header className={styles.header}>
              <a className={styles.logout}>
                Log Out
              </a>
            </header>
            <div className={styles.container}>
              <div className={styles.buttonwrapper}>
                <button className={styles.button} onClick={() => openWidget()}>
                  Upload file and Generate Link
                </button>
              </div>
              {url && (
                <div className={styles.formwrapper}>
                  <h5>Shareable link</h5>
                  <input disabled value={url} className={styles.input} />
                  <button className={styles.copy}>
                    {copy}
                  </button>
                </div>
              )}
            </div>
          </main>
        </div>
      );
    }
Enter fullscreen mode Exit fullscreen mode

Next, we need to create a function using the Clipboard API to copy the returned URL on our device clipboard.

    //imports here

    export default function Home() {
      //state here

      const openWidget = () => {
        //upload widget code
      };

      //add 
      const handleCopyToClip = () => {
        navigator.clipboard
          .writeText(url)
          .then(() => setCopy('Copied!'))
          .catch((err) => console.log('error copying to clipboard', err));
      };

      return (
        <div>
          <Helmet>
            <script src='https://upload-widget.cloudinary.com/global/all.js' />
          </Helmet>
          <main className={styles.files}>
            {/* header goes here */}  
            <div className={styles.container}>
              <div className={styles.buttonwrapper}>
                {/* button upload here */}  
              </div>
              {url && (
                <div className={styles.formwrapper}>
                  <h5>Shareable link</h5>
                  <input disabled value={url} className={styles.input} />
                  <button className={styles.copy} onClick={handleCopyToClip}>{/* modify this*/}
                    {copy}
                  </button>
                </div>
              )}
            </div>
          </main>
        </div>
      );
    }
Enter fullscreen mode Exit fullscreen mode

Finally, we can start a development server and test our application:

npm run dev
Enter fullscreen mode Exit fullscreen mode

upload button
select a file
copy link

Adding Authentication with Auth0

Setup a Developer Account

To get started, we need to log into our Auth0 dashboard. Click on Applications

Auth0 Dashboard

In the application page, click on the Create Application button, input application name auth0Cloudinary in our case, select Regular Web Application as the application type and Create

Creating a new app

Click on the Settings tab and copy the Domain, Client ID, and Client Secret.

copy credentials

Then scroll down to the Applications URIs section and fill in the details below for Allowed Callback URLs and Allowed Logout URLs, respectively.

  • Allowed Callback URLs
    • http://localhost:3000/api/auth/callback
  • Allowed Logout URLs
    • http://localhost:3000/ URIs

Then scroll down to the bottom of the page and click on the Save Changes button.

Integrating Auth0 with Next.js

To integrate Auth0 in our application, we need to create an .env.local file in our root directory and fill in the required parameters(Domain, Client ID and Client Secret) as shown below:

AUTH0_SECRET='LONG_RANDOM_VALUE'
AUTH0_BASE_URL='http://localhost:3000'
AUTH0_ISSUER_BASE_URL='https://YOUR_AUTH0_DOMAIN.auth0.com'
AUTH0_CLIENT_ID='YOUR_AUTH0_CLIENT_ID'
AUTH0_CLIENT_SECRET='YOUR_AUTH0_CLIENT_SECRET'
Enter fullscreen mode Exit fullscreen mode

We can run the snippet below on our terminal to generate a random secret for the AUTH0_SECRET

node -e "console.log(crypto.randomBytes(32).toString('hex'))"
Enter fullscreen mode Exit fullscreen mode

Next, we need to create a dynamic route file for our APIs. We need to navigate to the pages folder, inside this folder, navigate to the api folder, in the folder, create an auth folder, and inside this folder, create an […auth0].js file and add the snippet below:

    import { handleAuth } from '@auth0/nextjs-auth0';
    export default handleAuth();
Enter fullscreen mode Exit fullscreen mode

The handleAuth() function will generate APIs for:

  • Login
    • /api/auth/login
  • Logout
    • /api/auth/logout
  • Fetch user data
    • /api/auth/me
  • Redirect user on successful login.
    • /api/auth/callback

Next, we need to update _app.js file inside the pages folder as shown below:

    import { UserProvider } from '@auth0/nextjs-auth0';
    export default function App({ Component, pageProps }) {
      return (
        <UserProvider>
          <Component {...pageProps} />
        </UserProvider>
      );
    }
Enter fullscreen mode Exit fullscreen mode

Finally, we need to update index.js as shown below

    //other imports here
    import { withPageAuthRequired } from '@auth0/nextjs-auth0';

    export default function Home({ user }) { //update this with destructure props
      //state goes here

      //functions goes here

      return (
        <div>
          <Helmet>
            <script src='https://upload-widget.cloudinary.com/global/all.js' />
          </Helmet>
          <main className={styles.files}>
            <header className={styles.header}>
              <a href='/api/auth/logout' className={styles.logout}> {/* add href to this*/}
                Log Out
              </a>
            </header>
            <p className={styles.name}>Hi {user.name}</p> {/*add this*/}
            {/*remaining code snippet goes here*/}
          </main>
        </div>
      );
    }
    export const getServerSideProps = withPageAuthRequired({
      returnTo: '/',
    });
Enter fullscreen mode Exit fullscreen mode

In the snippet above, we imported withPageAuthRequired and also modified the log out link. We used Next.js inbuilt method getServerSideProps to call the withPageAuthRequired method, specified the URL to redirect to after login, accessed the user props it returned and then displayed the user’s name.

Complete index.js

Conclusion

This post discussed how to build and secure an application that supports file upload to Cloudinary, get sharable links, and share using the device clipboard.

You may find these resources useful:

Content created for the Hackmamba Jamstack Content Hackathon.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .