User Authentication
Learn how to enable, configure, and customize the isomorphic authentication system in JopiJS.
Overview
Security is a core pillar of JopiJS. Instead of reinventing the wheel, JopiJS provides a plug-and-play Authentication Module that handles everything from the login page to secure session management.
In this guide, you will learn how to:
- Activate the standard authentication module.
- Understand the secure Stateless Cookie mechanism.
- Customize the login UI and branding.
- Extend the user profile with custom fields (e.g., birthdate).
- Replace the default JSON user list with your own logic.
1. How Authentication Works
JopiJS uses an isomorphic, cookie-based authentication flow. This means the authentication state is available on both the server (SSR) and the browser.
- Identification: The user submits their credentials via a login form.
- Verification: The server calls a "Finder" logic (by default, checking a JSON file).
- Session Creation: If valid, JopiJS generates a Stateless Secure Cookie (Token).
- Automatic Persistence: The browser automatically attaches this cookie to every subsequent request (API or Page load). You don't need to manually manage headers or local storage.
Security Note:
The authentication cookie is signed (it cannot be altered) but its content is Base64 encoded (it can be read). Never store highly sensitive data like passwords or clear-text credit cards inside the user's public info.
2. Quick Setup
The easiest way to start is to use the official @jopijs/jopimod_user_auth module.
Installation
- Add the dependency:
npm install @jopijs/jopimod_user_auth - Verify Extraction:
JopiJS will automatically extract the module into
src/mod_jopijs@user_auth/via thepostinstallscript. If it doesn't appear, run:npx jopi mod-check
Default State
Once installed, your app instantly has:
- A
/loginroute. - A default user (
admin/admin). - A
user.gen.jsonfile in the module folder.
3. Customizing the System
JopiJS authentication is designed to be "Hackable by Design". Any shared item in the @jopijs/jopimod_user_auth module can be replaced or extended using the Priority System.
A. Branding & UI Examples
To change the visual identity of your login page, you can override the default image or form.
Example: Replacing the Login Image
src/ mod_my_theme/
└── @alias/res/jopijs.auth.image/
├── high.priority # Force this version
├── my_branded_bg.jpg # Your new image
└── index.ts # export default my_branded_bg;Example: Replacing the Login Form
src/ mod_my_theme/
└── @alias/ui/jopijs.auth.loginForm/
├── high.priority # Force this version
└── index.tsx # Your custom form componentexport default function CustomLoginForm() {
return (
<form method="POST" action="/login">
<h3>Custom Login</h3>
<input name="login" type="text" placeholder="Login" required />
<input name="password" type="password" placeholder="Password" required />
<button type="submit">Sign In</button>
</form>
);
}B. Backend Logic Examples (Custom Database)
By default, JopiJS looks for users in a JSON file. You can easily switch this to a real database (PostgreSQL, MongoDB, etc.) by overriding the findUser function.
Example: Custom PostgreSQL Finder
import IAuthData from "@/lib/jopijs.auth.IAuthData";
import UserEntry from "@/lib/jopijs.auth.IUserEntry";
import users from "@/res/jopijs.auth.userList";
export default async function(loginInfo: IAuthData): Promise<UserEntry | undefined> {
return (users as UserEntry[]).find(e => e.authInfos.login===loginInfo.login);
}Note: Ensure your mod_db_auth folder contains a high.priority file to activate this custom logic.
C. Logic: Password Verification
The checkAuthData function is responsible for validating the credentials submitted by the user. While the default implementation does a simple string comparison, you can override it to implement more secure checks, such as using hashing libraries (Argon2, bcrypt).
Example: Custom Password Checker
import IAuthData from "@/lib/jopijs.auth.IAuthData";
export default async function(fromDB: IAuthData, fromBrowser: IAuthData): Promise<boolean> {
// 'fromDB' is the data returned by your 'findUser' logic.
// 'fromBrowser' is the raw data submitted via the login form.
return fromDB.password === fromBrowser.password;
}The IAuthData interface (shared via @/lib/jopijs.auth.IAuthData) defines the structure of the credentials object:
interface IAuthData {
login: string;
password: string;
}D. Available Shared Items
| Category | Shared Path | Purpose | Behavior |
|---|---|---|---|
| UI | @/ui/jopijs.auth.loginForm | The actual login form. | Replace |
| Assets | @/res/jopijs.auth.image | The side-image shown on the login page. | Replace |
| Labels | @/translations/page.login | All labels and error messages. | Merge |
| Core | @/lib/jopijs.auth.findUser | Fetch a user from a DB. | Replace |
| Core | @/lib/jopijs.auth.checkAuthData | Validate credentials. | Replace |
4. Extending User Data
If you need to add custom metadata to your users (like a phone number or birthdate), JopiJS allows you to extend the global IUserInfos type seamlessly using Interface Merging.
For a detailed step-by-step tutorial, see the dedicated guide:
👉 Extend the User Object
5. Summary
- JopiJS provides a robust, isomorphic authentication module out of the box.
- Sessions are managed automatically via secure stateless cookies.
- Use the Priority System to swap the branding or database logic.
- Interface merging allows you to grow the
IUserInfostype as your application scales.
To learn more about checking roles and protecting specific routes, see the Limiting Access guide.