In this post, we will talk about how we can authenticate our SolidJS route with Solid Router. By the end of this article, we will understand what authentication is, and how it is used to guard routes in SolidJS using the Solid Router.
Overview
Route authentication has been around for a while, and is now a fundamental part of many applications like Facebook, Gmail, and Instagram. At this point, it’s about as ubiquitous as entering a password into a phone. Authentication through route guards in SolidJS can be achieved in two different ways. The first that we are going to cover in this app is the use of a Solid Router. The second is provided by Solid Start, which is more like a folder-based routing. It is a meta-framework (a framework built on another framework). At its core, it's powered by SolidJS and Solid Router. This article assumes that you have already set up a SolidJS project, and will not walk through that part of the process. If you have not yet spun up a project, you can find tutorials and instructions at SolidJS.
What is Authentication?
Authentication is a process of verifying the identity of the user. It provides access control to check if the user is authenticated. Authentication enables organizations to keep their networks secure by permitting only authenticated users or processes to gain access to their protected resources which, in our case, is the route in the application.
To learn more about authentication, you can check out the following;
What is a route guard?
A route guard helps to prevent unauthenticated users from accessing certain routes/parts in an application. For example, an app lock on a mobile phone can be seen as a guard. You may be able to access the phone and some apps, but you can’t access other apps.
We can also use parental controls on the phone, internet, and so on as a type of guard. You can read more about route guard.
Get Started
If you haven't installed the Solid Router in your project, run the command below:
# Using NPM
npm i @SolidJS/router
# Using PNPM
pnpm i @SolidJS/router
# Using yarn
yarn add @SolidJS/router
Solid Router is a universal router for SolidJS which works whether you're rendering on the client or the server. It was inspired by and combines the paradigms of React Router and the Ember Router. To read more, check out Solid Router
Having installed @SolidJS/router
, we will be creating the following files;
- Home: src/pages/Home.jsx
- Signin: src/pages/Signin.jsx
- Pricing: src/pages/Pricing.jsx
- RouteGuard: src/RouteGuard/index.jsx
- Header: src/components/Header/index.jsx
- Header styles: src/components/Header/Header.module.css
Terminologies
useNavigate
: a library provided to us by SolidJS Router that is used to navigate.createEffect
: creates a listener, which calls it's first argument when attached signals change.Outlet
: serves as the children associated with RouteGuard wrapperNavLink
: used to navigate between pages just likea tag
which takes a propertyhref
for the page route path.
Home page
Below is the code we will paste in our home page file. But if you already have a content for this, you don't have to change it.
import appstyles from '../App.module.css';
export default function Home() {
return (
<div class={appstyles.wrapper}>
<h2>Home page here</h2>
<p>You can see this because you are authenticated</p>
</div>
);
}
This is just a simple page component with a style imported at the top.
Signin page
This is the content of our sign in page, but if have a content already, that's fine. The most important contents are the logIn function and the createEffect.
import { useNavigate } from "@SolidJS/router";
import { createEffect } from "solid-js";
import appstyles from "../App.module.css";
export default function SignIn () {
const navigate = useNavigate();
const logIn = () => {
sessionStorage.setItem('token', 'mytokenisawesome');
navigate('/home', { replace: true });
};
createEffect(() => {
if(sessionStorage.getItem('token')) {
navigate('/home', { replace: true })
}
})
return (
<div class={appstyles.wrapper}>
<h2>Sign In Page</h2>
<p>You can see this because you are not authenticated</p>
<button class={appstyles.login_btn} onClick={logIn}>Log In</button>
</div>
)
}
The logIn function will be called when the login button is clicked. When clicked, a hardcoded token is saved in the session storage. The user is then navigated to the home page, which by default, can only be accessed if the user has been authenticated. The createEffect ensures the authenticated user won't access the sign in page.
Pricing page
This is just another page which, in this project, represents another protected page that could only be accessed by an authenticated user.
import appstyles from "../App.module.css";
export default function Pricing() {
return (
<div class={appstyles.wrapper}>
<h2>Pricing page here</h2>
<p>You can see this because you are authenticated</p>
<i>Here you can find all prices</i>
</div>
);
}
This is just a simple page component with a style imported at the top.
RouteGuard Component
import { Outlet, useNavigate } from "@SolidJS/router";
import { createEffect } from "solid-js";
import Header from "../components/Header";
export default function RouteGuard () {
const navigate = useNavigate();
const token = sessionStorage.getItem('token');
createEffect(() => {
if(!token) {
navigate('/signin', { replace: true });
}
})
return (
<div>
<Header />
<Outlet />
</div>
)
}
This serves as the wrapper for routes you want to protect from unauthenticated users. If there is no token in the session storage, the user will be routed to the signin page.
Header component
If you already have the content for the header file, you don't have to change it. All you need is the logOut function.
import { NavLink, useNavigate } from "@SolidJS/router";
import styles from './Header.module.css';
export default function Header() {
const navigate = useNavigate();
const logOut = () => {
sessionStorage.removeItem('token');
navigate('/signin', { replace: true });
}
return (
<div class={styles.container}>
<h3>My SolidJS App</h3>
<div class={styles.links}>
<NavLink href="/home">
Home
</NavLink>
<NavLink href="/pricing">
Pricing
</NavLink>
</div>
<button class={styles.logout_btn} onClick={logOut}>Log out</button>
</div>
);
}
Above, we have styles imported from the header CSS module, and we have a logOut function that is used to remove the token from the session storage and also navigate the user to the signin page.
Header styles
This is the style for the header component.
.container {
padding: 0.5rem 1rem;
background-color: rgb(34, 80, 130);
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
}
.links {
color: #fff;
display: flex;
flex: 1;
align-items: center;
justify-content: flex-end;
gap: 1rem;
font-size: 1rem;
text-decoration: none;
padding: 0 1.5rem;
text-transform: uppercase;
}
.links a:link, a {
color: #fff;
text-decoration: none;
}
.logout_btn {
padding: 0.5rem 0.86rem;
background-color: #fff;
color: rgba(255, 0, 0, 0.795);
text-transform: uppercase;
border: 0;
outline: none;
border-radius: 0.3rem;
font-weight: 800;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
}
.logout_btn:hover {
background-color: rgb(239, 236, 236);
}
Updates for src/App.jsx
This file is where the route is managed, and where we grouped certain routes we wanna guard or protect from unauthenticated users.
import { Route, Routes } from '@SolidJS/router';
import Home from './pages/Home';
import SignIn from './pages/Signin';
import RouteGuard from './RouteGuard';
import Pricing from './pages/Pricing';
function App() {
return (
<Routes>
<Route path="/signin" component={SignIn} />
<Route path="/" component={RouteGuard}>
<Route path="/home" component={Home} />
<Route path="/pricing" component={Pricing} />
</Route>
<Route path="*" element={()=> <div>Page Not found!!!</div>} />
</Routes>
);
}
export default App;
Update for src/App.module.css
.login_btn {
padding: 0.5rem 0.86rem;
background-color: rgb(34, 80, 130);
color: #fff;
text-transform: uppercase;
border: 0;
outline: none;
border-radius: 0.3rem;
font-weight: 800;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
}
.login_btn:hover {
background-color: rgb(27, 64, 104);
}
.wrapper {
padding: 1rem;
}
Updates for src/index.jsx
We have to update this file by wrapping our App with Router
/* @refresh reload */
import { render } from 'solid-js/web';
import { Router } from '@SolidJS/router';
import './index.css';
import App from './App';
render(() => (
<Router>
<App />
</Router>
), document.getElementById('root'));
Update for src/index.css
a.active, a:active {
text-decoration: underline !important;
text-decoration-line: underline;
text-decoration-style: wavy !important;
text-decoration-color: rgba(255, 0, 0, 0.795) !important;
text-underline-offset: 2px;
text-decoration-thickness: from-font;
}
Conclusion
In this article, we were able to understand what authentication is, and how to create a route guard which is very important when trying to prevent certain users from accessing certain routes in our application. I hope we have been able to help you see the possibilities of making use of SolidJS and Solid Router to build a well-authenticated application. If you are having issues, you can check out the repo for SolidJS-route-guard. Don't forget that you can see, in detail, how this was maximized in one of This Dot's open source project starter.dev GitHub Showcases.
Happy coding!