If you've been following along with the previous articles, you should have a working Login and Register page connected to Backendless. Now we're going to streamline these a bit and add in private routing through SvelteKit.
We haven't done much work on the homepage (index.svelte
) so we'll work on that now. Delete the default Svelte code and add in some base content. We'll add in the HTML first, then add in a new page called members.svelte
index.svelte
<script>
import {user} from "$lib/store";
</script>
<div class="container mx-auto">
<h1 class="text-5xl font-bold mb-3">Hello World</h1>
<p>
<a href={$user.email ? "/members" : "/login"} class="btn btn-outline">
Click here for the private route
</a>
</p>
</div>
You'll see that our link href
is wrapped in an if
statement. Each of your private routes should be written like this to make sure that the user is logged in. Alternatively, you could wrap the entire link tag in a Svelte {#if}{/if}
statement if you wanted to hide the button entirely. Any time you use a Svelte Store in your HTML content, you must use the reactive symbol $
. This lets the content know that this variable will change and to watch for updates.
You could also use this to append a target route to the login page, such as /login?target-route=members
and then redirect the user after login, but that will be in a different tutorial.
Create the Members Page
Now create a new page in your routes
folder called members.svelte
, and add in some base html for that as well.
members.svelte
<div class="container mx-auto">
<h1 class="text-5xl font-bold mb-3">Members Only!</h1>
<p><a href="/" class="btn btn-outline">Return Home</a></p>
</div>
In this page, we don't need to treat the anchor tags with fancy if
statements, since the site will see this as a private route, and hide it entirely to unauthenticated users.
Creating the Private Route System
With Backendless, when you log in, an Object is returned containing some of your user information. In our login.svelte
page, we're updating our global Svelte Store user
to be this value. Since this is global now, we can use it on our __layout.svelte
file to check whenever someone is navigating through site. If they land on a private page, and they aren't logged in, we gracefully redirect them back to the Login screen.
We will accomplish this by creating a list of what URLs should we want checked.
Note: This tutorial will be guiding you with the idea that we will be creating a members-only site. The assumption is that the majority of pages in your future web app will be private, and only a few will be public.
Creating the URL List
Inside your lib/data
folder, create a new file called publicRoutes.json
. Inside will be an array containing three pages to start. The homepage, the login page, and the register page. If an unauthenticated user lands on a page that is NOT in this list, they will be routed to the Login page.
publicRoutes.json
[
"/",
"/login",
"/register"
]
Any future public routes, like a blog post or Contact Us page, will go here in this list.
Validating the User
Inside your lib/functions
folder, create a new file called auth.js
. This will hold multiple functions for us in the future. For now, we want to create a new function called validateUserToken
. This function will be checking the Backendless token that's saved to your localStorage, and making sure that it has not expired or been tampered with.
We'll need to import a few things before we get started. Your initial file should look something like this (with some notes):
auth.js
import Backendless from "backendless";
import {user} from "$lib/store";
import publicRoutes from "$lib/data/publicRoutes.json";
import {goto} from "$app/navigation";
// Check if user is logged in
export const validateUserToken = async() => {
// Validate the user token
// If valid: Update the user store with the latest information
// If not valid: Unset the user store, and redirect to the login page
// If the token is corrupted, force logout and redirect user to the login page
}
The last import statement is a new one for the tutorial. The goto
function is from the Svelte library, and is an easy way to automatically route a user to another page. This is what we'll be using to send unauthenticated users to our Login page.
Note that we've made this an async call. We need this to finish processing before routing our user throughout the site. To get started, we'll create a Try/Catch and add in our first Backendless method to check for the token:
auth.js
export const validateUserToken = async() => {
try {
let response = await Backendless.UserService.getCurrentUser();
} catch(error) {
}
}
You don't need to pass anything to the getCurrentUser() method. The Backendless SDK already knows where to look for the token. This could return an object containing user data, a null
value if the user is not logged in, or an HTTP error code if the token is expired or corrupted.
auth.js
export const validateUserToken = async() => {
try {
let response = await Backendless.UserService.getCurrentUser();
if(response) {
// Valid user found
user.set(response);
} else {
// Unset the user store
user.set({});
}
} catch(error) {
}
}
Now to redirect the user if they are not logged in:
auth.js
export const validateUserToken = async() => {
try {
let response = await Backendless.UserService.getCurrentUser();
if(response) {
// Valid user found
user.set(response);
} else {
// Unset the user store
user.set({});
// Invalid user found. Grab their current location to match against the publicRoutes list
let currentLocation = window.location.pathname;
// This will redirect if the unauthenticated user is on a private route
if(!publicRoutes.includes(currentLocation)) {
await goto("/login?error=expired-token");
return false;
}
}
} catch(error) {
}
}
This uses a simple includes
to see if the user is on a private route. If they are, send them elsewhere. In this example, we're appending a query parameter so that we can show an alert bar on the Login page that their token has expired and to log back in. The return false;
will prevent anything else from firing in the function.
We'll do something very similar in the catch
portion, with an additional Backendless force-logout method just to make sure all the user data gets reset. That will look like this:
auth.js
export const validateUserToken = async() => {
try {
let response = await Backendless.UserService.getCurrentUser();
if(response) {
// Valid user found
user.set(response);
} else {
// Unset the user store
user.set({});
// Invalid user found. Grab their current location to match against the publicRoutes list
let currentLocation = window.location.pathname;
// This will redirect if the unauthenticated user is on a private route
if(!publicRoutes.includes(currentLocation)) {
await goto("/login?error=expired-token");
return false;
}
}
} catch(error) {
// User has invalid token, so log them out
await Backendless.UserService.logout();
await goto("/?error=expired-token");
return false;
}
}
For your site's specific needs, the catch
section may be useful for logging to an external analytics tool when a user's token is corrupted or expired.
Adding the Validation Check on the Layouts File
Inside the __layouts.svelte
file, we need to import the Svelte function onMount
, because we'll need to be validating the Backendless token saved to the user's localStorage when they log in. Since Svelte is a compiled language, checking anything browser related should always be inside an onMount
or inside a function call.
__layout.svelte
import {onMount} from 'svelte';
Since we want to make sure each route is checked before it's loaded, we want our onMount
to be asynchronous, so we can use the await
property. Start the onMount
call like so:
__layout.svelte
onMount(async() => {
// Code coming soon
}
Import our new validateUserToken
and add it inside the onMount()
call:
__layout.svelte
import {validateUserToken} from "$lib/functions/auth";
onMount(async() => {
await validateUserToken();
})
To see how this works now, make sure you are logged out of your backendless account by clearing your localStorage, and manually going to the /members
route. If everything was set up successfully, you should be routed directly to the Login page.
You may have seen a flash of content though, and that's what we'll be fixing next.
Note: This next part is optional. If you don't mind the flashing of content then you can skip this next step
Restricting content
Still in the __layouts.svelte
file, create a new variable above the onMount
named:
__layout.svelte
let isSiteReadyToLoad = false;
And in our content, we will be wrapping all the content in an if
statement to hide it. This also gives us the option to add in a nice Svelte animation to make the content fade in.
You could also add an else
here and add a loading icon if you have one.
Your HTML should look something like this now:
__layout.svelte
{#if isSiteReadyToLoad}
{#if $user.email}
<h1>Welcome, User</h1>
{:else}
<h1>Please login</h1>
{/if}
<slot></slot>
{/if}
Optional Animation Effect
And to add the Svelte animation, import the fade
function at the top of your <script>
tag
__layout.svelte
import { fade } from 'svelte/transition';
To animate an element in Svelte, you need two things:
- A conditional (such as our
isSiteReadyToLoad
) - A standard HTML tag like a
<div>
. Svelte animation properties do not work on Svelte Components.
Your HTML should be structured like so:
__layout.svelte
{#if isSiteReadyToLoad}
<div transition:fade>
{#if $user.email}
<h1>Welcome, User</h1>
{:else}
<h1>Please login</h1>
{/if}
<slot></slot>
</div>
{/if}
To finish off the Restricted Content section, we can set the value of our isSiteReadyToLoad
to true after the validateUserToken()
function has completed:
__layout.svelte
// Hide the site content until it is fully loaded
let isSiteReadyToLoad = false;
onMount(async() => {
await validateUserToken();
// Load the site
isSiteReadyToLoad = true;
})
Now if you manually navigate to /members
it should fade in the Login page, and you will never see the Members content.
To fully test our new private route, lets log into the site with the username and password that we created back in Tutorial #1. Once you are logged in, you should see your "Welcome, User" at the top left part of your screen. Now manually navigate to the homepage and click on the Members button.
Top comments (1)
is there a repo somewhere that i can clone as i am stuck on the tailwind.config.cjss