Authentication
Harpia provides a robust, built-in Authentication ecosystem via its CLI generator, allowing you to implement a complete JSON Web Token (JWT) Bearer or Cookie-based Session authentication system in mere seconds.
Generating the Auth Module
To scaffold the authentication system within your application, utilise the Harpia CLI generator from the root of your project:
bun harpia generate setup
When you run this command, the CLI will prompt you to choose your preferred authentication strategy:
- Session: Generates a complete Session-based authentication system (Cookie-based), ideal for traditional monolithic Fullstack web applications.
- Bearer: Generates a JWT (JSON Web Token) authentication system, ideal for APIs and Single Page Applications.
If you select the Bearer strategy, you will be prompted to choose a token storage provider:
- Database (Prisma): Stores token sessions in your relational database, automatically generating a
Tokenmodel in yourschema.prisma. - Redis: Stores token sessions in memory via Redis for high-performance retrieval and expiration.
What gets generated?
The generator scaffolds an entire auth (or session) module under app/modules/, along with necessary configuration files:
app/config/auth.tsorapp/config/jwt.ts: Central configuration files defining secrets, expiration times, and storage behavior.app/modules/.../controllers/: Contains thestore(login),show(me/profile), anddestroy(logout) controllers.app/modules/.../services/: The business logic for verifying credentials and handling tokens or sessions.app/middlewares/auth.ts: A robust middleware providing theisAuthenticatedguard to protect your routes.
Protecting Routes
Once generated, you can seamlessly protect any route in your application by importing and attaching the isAuthenticated middleware to your router definitions.
import { Router } from "@harpiats/core";
import { userController } from "./controllers";
import { isAuthenticated } from "app/middlewares/auth";
const userRoutes = new Router("/users");
// This route is public
userRoutes.get("/", userController.index);
// This route requires authentication
userRoutes.get("/profile", isAuthenticated, userController.show);
export { userRoutes };
Accessing the Authenticated User
When a request passes through the isAuthenticated middleware, you can retrieve the current user’s session or token data using the provided utilities within your controllers.
Using Bearer (JWT)
// app/modules/users/controllers/show.ts
import type { Request, Response } from "@harpiats/core";
import { ApiResponse, AppError } from "@harpiats/common";
import { jwt } from "app/config/jwt";
export const show = async (request: Request, response: Response) => {
try {
// Extract user from the JWT Bearer token in the request header
const user = await jwt.fromRequest(request);
if (!user) {
throw AppError.E_UNAUTHORIZED();
}
return ApiResponse.success(response, user);
} catch (error: any) {
return ApiResponse.error(response, error);
}
};
Using Session
// app/modules/users/controllers/show.ts
import type { Request, Response } from "@harpiats/core";
import { ApiResponse, AppError } from "@harpiats/common";
import { session } from "app/config/session";
export const show = async (request: Request, response: Response) => {
try {
// Extract user from the HttpOnly Session cookie
const user = await session.fromRequest(request);
if (!user) {
throw AppError.E_UNAUTHORIZED();
}
return ApiResponse.success(response, user);
} catch (error: any) {
return ApiResponse.error(response, error);
}
};
Inside WebSockets
If you are building real-time features, you can also easily authenticate WebSocket connections. The Session and JWT utilities provide a fromWebSocket method specifically for this purpose.
import harpia from "@harpiats/core";
import { session } from "app/config/session"; // or import { jwt } from "app/config/jwt";
const app = harpia();
app.ws("/chat", {
async open(ws) {
// Retrieve the authenticated user from the WebSocket connection
const user = await session.fromWebSocket(ws); // or await jwt.fromWebSocket(ws)
if (!user) {
// Reject unauthorized connections immediately
return ws.close(1008, "Unauthorized");
}
// Store user data in the typed ws.data for use in other handlers
ws.data.userId = user.id;
ws.send(`Welcome to the chat!`);
},
message(ws, message) {
console.log(`User ${ws.data.userId} sent: ${message}`);
}
});
Configuration
You can tweak the authentication behaviour by editing the generated files in app/config/.
Ensure you define strong environment variables (e.g., JWT_SECRET) in your .env file before deploying your application to production.
JWT_SECRET=your_super_secret_string_here