Part 1: Setup & JWT Basics

If you haven't already, I would recommend having a quick look at the Introduction & Sequence Diagram
Welcome to the 3-part series that helps you create a scalable production-ready authentication system using pure JWT & a middleware for your SvelteKit project
Part 1: Setup & JWT Basics
You are reading Part 1
Goal: Set up the project and core components for JWT authentication
Topics we'll cover
Tech Stack & Prerequisites: SvelteKit, TypeScript, database (e.g., PostgreSQL), bcrypt, jsonwebtoken
Setup: Create a SvelteKit project & install dependencies
Database Schema: users and jwt_token_logs tables
Database Interface: Functions like createUser, getUserByEmail, logJwtToken
JWT Utilities: Implement generateToken, verifyToken, logToken, and authenticateRequest functions along with cookie module
Tech Stack & Prerequisites
Our implementation uses:
SvelteKit
TypeScript
YOUR_DATABASE_CHOICE (PostgreSQL, MySQL, etc.)
bcrypt (for password hashing)
jsonwebtoken (for JWT handling)
Setup
First, let's create a new SvelteKit project and install the necessary dependencies:
# Create a new SvelteKit project
npm create svelte@latest my-auth-app
cd my-auth-app
# Install dependencies
npm install bcrypt jsonwebtoken
npm install --save-dev @types/bcrypt @types/jsonwebtoken
# Install database driver
npm install [YOUR_DATABASE_DRIVER]
If the above code doesn't work, don't worry!
You have two alternatives.
Create a project from SvelteKit official documentation
Ask ChatGPT how to create a Sveltekit project with a database & follow the steps
Database Schema
We'll need two tables:
users
- Stores user informationjwt_token_logs
- Logs JWT issuance for audit purposes (not for validation)
Users Table
-- users.sql
CREATE TABLE USERS (
USER_ID VARCHAR(100) PRIMARY KEY,
EMAIL VARCHAR(255) NOT NULL UNIQUE,
PASSWORD VARCHAR(255) NOT NULL,
ROLE VARCHAR(50) NOT NULL DEFAULT 'user',
CREATED_AT DATETIME DEFAULT GETDATE(),
LAST_LOGIN DATETIME NULL
);
JWT Token Logs Table
-- jwt_token_logs.sql
CREATE TABLE JWT_TOKEN_LOGS (
LOG_ID UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(), -- Unique ID for the log entry
USER_ID VARCHAR(100) NOT NULL, -- User the token was issued to
TOKEN_VALUE TEXT NOT NULL, -- The JWT itself. Consider security implications of logging the full token.
ISSUED_AT DATETIME NOT NULL, -- Timestamp from within the JWT 'iat' claim
EXPIRES_AT DATETIME NOT NULL, -- Timestamp from within the JWT 'exp' claim
LOGGED_AT DATETIME DEFAULT GETDATE(), -- Timestamp when this log entry was created
ADDITIONAL_DATA TEXT -- Optional: Store context like IP address, user agent, etc.
);
Database Interface
Create a database interface to interact with our tables:
// src/database/db.ts
import crypto from "crypto";
import bcrypt from "bcrypt";
// [INSERT YOUR DATABASE CONNECTION SETUP CODE HERE]
...
export async function createUser(
email: string,
password: string,
role: string = "user"
): Promise<any> {
try {
const userId = crypto.randomUUID();
// Start a transaction to ensure atomicity
...
try {
// Step 1: Insert the user in database
...
// Step 2: Get the inserted user from database
...
// Commit the transaction
...
// Return user
return user;
} catch (error) {
// If any error occurs, roll back the transaction
await transaction.rollback();
throw error;
}
} catch (error) {
console.error("Error creating user:", error);
return null;
}
}
export async function getUserByEmail(email: string): Promise<any> {
try {
// Fetch the user by email from database
const result = await ...
// Return user
return result.recordset[0] || null;
} catch (error) {
console.error("Error fetching user:", error);
return null;
}
}
export async function validateUserCredentials(
email: string,
password: string
): Promise<any> {
try {
// Get user by email from database
const user = await getUserByEmail(email);
// If no user found with this email
if (!user) {
return null;
}
// Compare provided password with stored hash
const isPasswordValid = await bcrypt.compare(
password,
user.PASSWORD
);
if (!isPasswordValid) {
return null;
}
// Update last login time of the user in database
await updateLastLogin(user.USER_ID);
// Return user without password
const { PASSWORD, ...userWithoutPassword } = user;
return userWithoutPassword;
} catch (error) {
console.error("Error validating credentials:", error);
return null;
}
}
export async function updateLastLogin(
userId: string
): Promise<boolean> {
try {
// Update last login based on userId in database
...
return true;
} catch (error) {
console.error("Error updating last login:", error);
return false;
}
}
export async function logJwtToken(
userId: string,
tokenValue: string,
issuedAt: Date,
expiresAt: Date
): Promise<void> {
try {
// log JWT token in database
...
} catch (error) {
console.error("Error logging JWT token:", error);
}
}
JWT Utilities
Create a JWT utility module:
// src/lib/auth/jwt.ts
import jwt from "jsonwebtoken";
import type { Cookies } from "@sveltejs/kit";
import { dev } from "$app/environment";
import { logJwtToken } from "$lib/database/db";
import { getAuthToken } from "./cookies";
// Re-export cookie functions for backward compatibility
export { setAuthCookie, clearAuthCookies } from "./cookies";
// Types
export interface JwtPayload {
userId: string;
email: string;
role?: string;
iat?: number;
exp?: number;
}
interface TokenResponse {
success: boolean;
message?: string;
user?: Omit<JwtPayload, "iat" | "exp">;
error?: any;
}
// Configuration
const JWT_SECRET =
process.env.JWT_SECRET ||
"your-fallback-secret-key-change-in-production";
const ACCESS_TOKEN_EXPIRY = "1h"; // 1 hour
/**
* Generate a JWT token for a user
*/
export const generateToken = (
payload: Omit<JwtPayload, "iat" | "exp">
): string => {
return jwt.sign(payload, JWT_SECRET, {
expiresIn: ACCESS_TOKEN_EXPIRY,
});
};
/**
* Verify the validity of a JWT token
*/
export const verifyToken = (token: string): TokenResponse => {
try {
const decoded = jwt.verify(token, JWT_SECRET) as JwtPayload;
return {
success: true,
user: {
userId: decoded.userId,
email: decoded.email,
role: decoded.role,
},
};
} catch (error) {
return {
success: false,
message:
error instanceof Error ? error.message : "Invalid token",
error,
};
}
};
/**
* Log JWT token issuance to database
*/
export const logToken = async (
token: string,
userId: string
): Promise<void> => {
try {
// Decode token to get claims without verification
const decoded = jwt.decode(token) as JwtPayload;
const issuedAt = new Date((decoded.iat || 0) * 1000);
const expiresAt = new Date((decoded.exp || 0) * 1000);
// Use the database function to log the token
await logJwtToken(userId, token, issuedAt, expiresAt);
} catch (error) {
console.error("Error logging token:", error);
// Don't throw, as token logging failure shouldn't break authentication
}
};
/**
* Handle token-based authentication
* Returns user info if authenticated or null if not
*/
export const authenticateRequest = (
cookies: Cookies
): TokenResponse => {
const accessToken = getAuthToken(cookies);
if (!accessToken) {
return {
success: false,
message: "No authentication token found",
};
}
return verifyToken(accessToken);
};
Create a cookie module.
// src/lib/auth/cookies.ts
import type { Cookies } from "@sveltejs/kit";
import { dev } from "$app/environment";
// Configuration for authentication cookies
export const COOKIE_OPTIONS = {
path: "/",
httpOnly: true,
secure: !dev, // Only use secure in production
sameSite: "strict" as const,
maxAge: 60 * 60, // 1 hour in seconds
};
/**
* Set authentication cookie in the response
*/
export const setAuthCookie = (
cookies: Cookies,
accessToken: string
): void => {
cookies.set("access_token", accessToken, COOKIE_OPTIONS);
};
/**
* Clear authentication cookies
*/
export const clearAuthCookies = (cookies: Cookies): void => {
cookies.delete("access_token", { path: "/" });
cookies.delete("user", { path: "/" }); // Clear the existing user cookie as well
};
/**
* Get authentication token from cookies
*/
export const getAuthToken = (
cookies: Cookies
): string | undefined => {
return cookies.get("access_token");
};
Next → Part 2: Authentication Flows
Previous → Introduction & Sequence Diagram
Subscribe to my newsletter
Read articles from Mukesh Jaiswal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Mukesh Jaiswal
Mukesh Jaiswal
Driving business growth with AI Automation (as Business Automation Partner) | Helping startups build (as Software Developer)