In this article, you will be learning how to create a personal image gallery using VueJS, Auth0 and Cloudinary API. With this application, users will be able to sign in using the authorization and authentication features of Auth0, create a profile and upload and download images with the help of Cloudinary.
You might be looking for the complete code. I've created a GitHub repo where you can find the complete code for the app.
Setting up Auth0
Auth0 is a flexible, drop-in solution to add authentication and authorization services to your applications. You are able to avoid the cost, time, and risk that come with building your own solution to authenticate and authorize users.
To set up Auth0, the first thing you will be needing is an Application in Auth0, so head on over to their website and create an account.
After setting up your account, the next thing you want to do is create your first application. You can do that by clicking the applications item on the menu on the left side and click “Create Application”.
From the image above, we see what that dropdown looks like;
- The first arrow points to the name box; here you can enter the name for your app and in this example, we’ll be calling it “My App”.
- The second arrow points to the application type options available, and for this project we’ll be using the “Single Page Web Applications” option, because just like the description says, we’ll be building a JavaScript front-end app that uses an API.
- When that’s done, click “Create”.
This sets up the application with default configuration for a Single Page Web Application. This configuration can be tweaked or changed later if you need or want to.
In the settings, add http://localhost:3000 to the Allowed Callback URLs, Allowed Logout URLs, Allowed Web Origins and Allowed Origins (CORS) fields. This is due to the fact that the sample client will be running on http://localhost:3000 you'll need to add the appropriate values if you're running on a server or a different port or on a host that isn't localhost
The rest of the defaults can be left as is.
From the image above, we see what that dropdown looks like;
- The first arrow points to the name box; here you can enter the name for your app and in this example, we’ll be calling it “My App”.
- The second arrow points to the application type options available, and for this project we’ll be using the “Single Page Web Applications” option, because just like the description says, we’ll be building a JavaScript front-end app that uses an API.
- When that’s done, click “Create”.
This sets up the application with default configuration for a Single Page Web Application. This configuration can be tweaked or changed later if you need or want to.
In the settings, add http://localhost:3000 to the Allowed Callback URLs, Allowed Logout URLs, Allowed Web Origins and Allowed Origins (CORS) fields. This is due to the fact that the sample client will be running on http://localhost:3000 you'll need to add the appropriate values if you're running on a server or a different port or on a host that isn't localhost
The rest of the defaults can be left as is.
Setting up Cloudinary
Cloudinary is a media management platform for web and mobile developers. Cloudinary is an end-to-end image- and video-management solution for websites and mobile apps, covering everything from image and video uploads, storage, manipulations, optimizations to delivery.
First, just like we did with Auth0, we have to create an account on Cloudinary.
When creating an account on Cloudinary, it’s important to choose the option that best describes your use for it. Here, we’ll be making use of their API to manipulate media on our project, so it’s only appropriate that we select the option “Programmable Media for image and video API”. When that’s over and done, you can then proceed to creating your account.
When your account is created and fully functional, you should see your dashboard, just like the one above.
Log into your Cloudinary account and set up an upload preset that will be handling the images being uploaded. Before setting up an upload preset, you’ll first have to create a media library folder that will hold the images being uploaded. You can do this using the following steps;
- Go to the media library.
- Create a new folder. we’ll be calling our folder “My images”.
Now, Cloudinary gives developers a tool that allows us integrate it in VueJS applications, called the VueJS SDK. The Cloudinary Vue.js SDK serves as a layer on top of Cloudinary's JavaScript (Cloudinary-core) library.
We can install the SDK by running the following command:
npm install cloudinary-vue
Then we can proceed with creating the upload preset.
Go to the Settings option and click Upload (tab)
Scroll down to and click on add upload preset.
Set the Upload preset name, select the Unsigned signing mode and input the name of the folder that you created above. In this case, “my images”
Set the Upload preset name, select the Unsigned signing mode and input the name of the folder that you created above. In this case, “my images”
It’s important to note that Unsigned upload presets are used when implementing upload capabilities from client-side apps i.e. allowing uploads straight from the browser.
Go to Upload control and switch the return delete token on if you’d like to be able to delete uploaded images.
Note: You can only use the delete token to delete an uploaded file within a span of ten minutes after uploading it.
Set up the rest of the settings to your preferences and click on 'save' to save this new upload preset.
Back on the settings page and upload tab the new upload preset will be listed amongst the existing presets.
Note: You can only use the delete token to delete an uploaded file within a span of ten minutes after uploading it.
Set up the rest of the settings to your preferences and click on 'save' to save this new upload preset.
Back on the settings page and upload tab the new upload preset will be listed amongst the existing presets.
The Vue Component
So to create the Vue Component that will allow users upload images to Cloudinary through the created upload preset.
The Template
The template below contains the file upload button that will trigger the uploader modal. We can put in a Main.vue
file.
<template>
<div>
<Header/>
<v-card>
<div class="helldo">
<input type="file" @change="onFileSelected">
<v-btn v-if="displayUploadBtn" outlined @click="uploadFile"> Upload </v-btn >
</div>
<v-tabs
color="deep-purple accent-4"
right
>
<v-tab>Landscape</v-tab>
<v-tab>City</v-tab>
<v-tab>Abstract</v-tab>
<v-tab-item
v-for="n in 3"
:key="n"
>
<v-container fluid>
<v-row>
<v-col
v-for="i in 6"
:key="i"
cols="12"
md="4"
>
<v-img
:src="`https://picsum.photos/500/300?image=${i * n * 5 + 10}`"
:lazy-src="`https://picsum.photos/10/6?image=${i * n * 5 + 10}`"
aspect-ratio="1"
></v-img>
</v-col>
</v-row>
</v-container>
</v-tab-item>
</v-tabs>
</v-card>
</div>
</template>
The :src attributes on line 35 and 36 serves to generate random images from Picsum.
Installing Axios
To make promise-based HTTP requests, I’d be installing Axios.
#install axios
npm i axios
After installing Axios, I can then use it to make request to the Cloudinary API.
<script>
import axios from "axios"
import Header from '../Header/Main.vue'
export default {
name: "home",
data: () => ({
displayUploadBtn: false,
selectedFile: null,
CLOUDINARY_URL: "https://api.cloudinary.com/v1_1/yungscript/upload",
CLOUDINARY_PRESET: "hk7esqdc" //your Cloudinary preset
}),
components: {
Header
},
methods: {
onFileSelected(event) {
this.selectedFile = event.target.files[0]
this.displayUploadBtn = true
},
uploadFile() {
let file = this.selectedFile
let formData = new FormData()
console.log(file);
formData.append("file", file)
formData.append("upload_preset", this.CLOUDINARY_PRESET)
axios({
url: this.CLOUDINARY_URL,
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: formData
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
})
}
}
};
</script>
Now the CLOUDINARY_URL is the URL that contains the images uploaded by the user.
Integrating Auth0
From the account you created above, you’d be given a domain name and a client ID. Your domain is the base URL that you will use to access the Auth0 APIs and the URL where you'll redirect users to log in, while the client ID is an alphanumeric string, and it's the unique identifier for your application. You cannot modify the Client ID. You will use the Client ID to identify the Auth0 Application to which the Auth0 SPA SDK needs to connect.
Now, you’ll create an auth_config.json file and add both your client ID and Domain.
{
"domain": "YOUR_AUTH0_DOMAIN",
"clientId": "YOUR_AUTH0_CLIENT_ID"
}
Now, Execute the following command to install the Auth0 SPA SDK:
npm install @auth0/auth0-spa-js
Create an auth directory within the src directory:
mkdir src/auth
Create an index.js file within the src/auth directory:
touch src/auth/index.js
Populate index.js with the following code:
/**
* External Modules
*/
import Vue from 'vue';
import createAuth0Client from '@auth0/auth0-spa-js';
/**
* Vue.js Instance Definition
*/
let instance;
export const getInstance = () => instance;
/**
* Vue.js Instance Initialization
*/
export const useAuth0 = ({
onRedirectCallback = () =>
window.history.replaceState({}, document.title, window.location.pathname),
redirectUri = window.location.origin,
...pluginOptions
}) => {
if (instance) return instance;
instance = new Vue({
data() {
return {
auth0Client: null,
isLoading: true,
isAuthenticated: false,
user: {},
error: null,
};
},
methods: {
Now to handle the callback when logging in using a redirect.
/** Handles the callback when logging in using a redirect */
async handleRedirectCallback() {
this.isLoading = true;
try {
await this.auth0Client.handleRedirectCallback();
this.user = await this.auth0Client.getUser();
this.isAuthenticated = true;
} catch (error) {
this.error = error;
} finally {
this.isLoading = false;
}
},
In cases where the user forget the password or token and might have to retrieve or renew it, we can then use the following to allow for token retrieval and redirect the user back to the login page.
/** Authenticates the user using the redirect method */
loginWithRedirect(options) {
return this.auth0Client.loginWithRedirect(options);
},
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
logout(options) {
return this.auth0Client.logout(options);
},
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
getTokenSilently(o) {
return this.auth0Client.getTokenSilently(o);
},
},
/** Use this lifecycle method to instantiate the SDK client */
async created() {
this.auth0Client = await createAuth0Client({
...pluginOptions,
domain: pluginOptions.domain,
client_id: pluginOptions.clientId,
audience: pluginOptions.audience,
redirect_uri: redirectUri,
});
try {
// If the user is returning to the app after authentication..
if (
window.location.search.includes('code=') &&
window.location.search.includes('state=')
) {
const { appState } = await this.auth0Client.handleRedirectCallback();
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
onRedirectCallback(appState);
}
} catch (error) {
this.error = error;
} finally {
// Initialize our internal authentication state
this.isAuthenticated = await this.auth0Client.isAuthenticated();
this.user = await this.auth0Client.getUser();
this.isLoading = false;
}
},
});
return instance;
};
/**
* Vue.js Plugin Definition
*/
// Create a simple Vue plugin to expose the wrapper object throughout the application
export const Auth0Plugin = {
install(Vue, options) {
Vue.prototype.$auth = useAuth0(options);
},
};
Routing
The following block of code serves to tell the browser what page to load. From the four components created, I can import and reference them as below;
import Vue from 'vue'
import VueRouter from 'vue-router'
// import Home from '../views/Home.vue'
// import Profile from "../components/Profile/Profile.vue";
const Home = () => import(/* webpackChunkName: "about" */ '../components/Home/Main.vue') //imports the Home Component
const About = () => import(/* webpackChunkName: "about" */ '../components/About/Main.vue') //imports the About Component
const Profile = () => import(/* webpackChunkName: "about" */ '../components/Profile/Main.vue') //imports the Profile Component
const Media = () => import(/* webpackChunkName: "about" */ '../components/Media/Main.vue') //imports the Media component
import { authGuard } from "../auth/authGuard";
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
// redirect: "/home",
component: Home
},
{
path: '/about',
name: 'About', //go to About Page
component: About
},
{
path: "/profile",
name: "Profile", // go to Profile Page
component: Profile,
beforeEnter: authGuard //before user goes to profile page, it checks if user is logged in. If not, it redirects the user to the login page.
},
{
path: "/media",
name: "Media", // go to Profile Page
component: Media
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
Plugins
I added the Vuetify package to style the project but feel free to use what you like. Bootstrap works just fine as well. Install Vuetify in your Vue app after creating your Vue project using Vue CLI like this;
vue add vuetify
Import Vue into your project as such:
import Vue from 'vue';
import Vuetify from 'vuetify/lib/framework';
Vue.use(Vuetify);
export default new Vuetify({
});
Voila, you’ve successfully integrated Cloudinary and Auth0 into your VueJS project.
Content created for the Hackmamba Jamstack Content Hackathon with Auth0 and Cloudinary.