
Posted on • Edited on
FullStack JWT Authentication and Authorization System with Django and SvelteKit - Part 3
Intoduction
Having introduced SvelteKit and our project's structure in the previous article of this series, it's time we built something.
Source code
The overall source code for this project can be accessed here:
Sirneij / django_svelte_jwt_auth
A robust and secure Authentication and Authorization System built with Django and SvelteKit
django_svelte_jwt_auth
This is the codebase that follows the series of tutorials on building aFullStack JWT Authentication and Authorization System with Django and SvelteKit.
This project was deployed on heroku (backend) and vercel (frontend) and its live version can be accessedhere.
To run this application locally, you need to run both thebackend
andfrontend
projects. While the latter has some instructions already for spinning it up, the former can be spinned up following the instructions below.
Run locally
To run locally
Clone this repo:
git clone https://github.com/Sirneij/django_svelte_jwt_auth.git
Change directory into the
backend
folder:cd backend
Create a virtual environment:
pipenv shell
You might opt for other dependencies management tools such as
virtualenv
,poetry
, orvenv
. It's up to you.Install the dependencies:
pipenv install
Make migrations and migrate the database:
python manage.py makemigrations python manage.py migrate
Finally, run the application:
python manage.py runserver
Live version
This project was deployed on heroku (backend) and vercel (frontend) and its live version can be accessedhere.
Step 1: Make the layout
Since our entire app will have some uniformity in terms of navigation and footer, let's populate our routes'__layout.svelte
with:
<scriptlang="ts">import{notificationData}from'../store/notificationStore';import{fly}from'svelte/transition';importHeaderfrom'../components/Header/Header.svelte';import'../dist/css/style.min.css';</script><Header/>{#if$notificationData}<divclass="notification-container"><pclass="notification"in:fly={{x:200,duration:500,delay:500}}out:fly={{x:200,duration:500}}>{$notificationData}</p></div>{/if}<main><slot/></main><footer><p>Visit<ahref="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit. Coded by<ahref="https://github.com/Sirneij/">JohnO.Idogun</a></p></footer>
It's a basic structure which hasHeader
component,footer
, display of notifications, and aslot
tag to take in other pages' contents. Auto-subscription ofnotificationData
was done by appending$
at it's beginning.notificationData
is a writable store with the following definition instores/notificationStore.ts
:
import{writable}from"svelte/store";exportconstnotificationData=writable("");
It expects a string value.Header
is a component that houses the app's navigation and has the following content incomponents/Header/Header.svelte
:
<scriptlang="ts">import{page}from'$app/stores';importlogofrom'./svelte-logo.svg';importjohnfrom'./john.svg';import{userData}from'../../store/userStore';import{logOutUser}from'$lib/requestUtils';</script><header><divclass="corner"><ahref="https://kit.svelte.dev"><imgsrc={logo}alt="SvelteKit"/></a></div><nav><svgviewBox="0 0 2 3"aria-hidden="true"><pathd="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z"/></svg><ul><liclass:active={$page.url.pathname==='/'}><asveltekit:prefetchhref="/">Home</a></li>{#if!$userData.username}<liclass:active={$page.url.pathname==='/accounts/login'}><asveltekit:prefetchhref="/accounts/login">Login</a></li><liclass:active={$page.url.pathname==='/accounts/register'}><asveltekit:prefetchhref="/accounts/register">Register</a></li>{:else}<li>Welcome,<asveltekit:prefetchhref="/accounts/user/">{$userData.username}</a></li><li><ahref={null}on:click={logOutUser}style="cursor: pointer;">Logout</a></li>{/if}</ul><svgviewBox="0 0 2 3"aria-hidden="true"><pathd="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z"/></svg></nav><divclass="corner"><ahref="https://github.com/Sirneij/"><imgsrc={john}alt="John O. Idogun"/></a></div></header>
This component introduces a couple important imports:
page
: To keep track of the current page, we imported the built-inpage and utilizing itsurl
object, we dynamically addedactive
classes to the navigation items.page
store contains an object with the currenturl
,params
,stuff
,status
anderror
.logo
andjohn
are just images which are in the same directory as theHeader.svelte
file.userData
: Just likenotificationData
,userData
is a custom writable store exported fromstores/userStore.ts
to make available current user's data. It has the following definition:
import{writable}from"svelte/store";exportconstuserData=writable({});
These data are updated/set during login and logout operations.
logOutUser
is one of the many functions domiciled in thelib/requestUtils.ts
file. It's purpose is to log the current user out and subsequently reset theuserData
to an empty object. The implementation is shown below:
//lib -> requestUtils.ts...exportconstlogOutUser=async()=>{constres=awaitfetch(`${BASE_API_URI}/token/refresh/`,{method:'POST',mode:'cors',headers:{'Content-Type':'application/json'},body:JSON.stringify({refresh:`${browserGet('refreshToken')}`})});constaccessRefresh=awaitres.json();constjres=awaitfetch(`${BASE_API_URI}/logout/`,{method:'POST',mode:'cors',headers:{Authorization:`Bearer${accessRefresh.access}`,'Content-Type':'application/json'},body:JSON.stringify({refresh:`${browserGet('refreshToken')}`})});if(jres.status!==204){constdata=awaitjres.json();consterror=data.user.error[0];throw{id:error.id,message:error};}localStorage.removeItem('refreshToken');userData.set({});notificationData.set('You have successfully logged out.')awaitgoto('/accounts/login');};
From the snippet, we made the first POST request toBASE_API_URI//token/refresh/
sending the current user'srefresh
token. This request returns the user'saccess
token which was used asAuthorization
header for the/logout/
endpoint. This process is required as only authenticated users can logout. If the response is successful, we removerefreshToken
from the localStorage, resetuserData
, setnotificationData
to something informative, and then redirect the user toaccounts/login
page. That's basically it! Some notable helper functions are thebrowserSet
andbrowserGet
which help set/save and get from the localStorage. Their implementations ain't hard to decipher:
import{browser}from'$app/env';...exportconstbrowserGet=(key:string):string|undefined=>{if(browser){constitem=localStorage.getItem(key);if(item){returnitem;}}returnnull;};exportconstbrowserSet=(key:string,value:string):void=>{if(browser){localStorage.setItem(key,value);}};
We utilized the built-inbrowser
to ensure we are in the browser environment before setting and getting items from the localStorage.
That is it for this part. Up next is how we handled registrations and user logins. Stay with me...
Outro
Enjoyed this article, considercontacting me for a job, something worthwhile or buying a coffee ☕. You can also connect with/follow me onLinkedIn.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse