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.

  1. Identification: The user submits their credentials via a login form.
  2. Verification: The server calls a "Finder" logic (by default, checking a JSON file).
  3. Session Creation: If valid, JopiJS generates a Stateless Secure Cookie (Token).
  4. 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

  1. Add the dependency:
    npm install @jopijs/jopimod_user_auth
  2. Verify Extraction: JopiJS will automatically extract the module into src/mod_jopijs@user_auth/ via the postinstall script. If it doesn't appear, run:
    npx jopi mod-check

Default State

Once installed, your app instantly has:

  • A /login route.
  • A default user (admin / admin).
  • A user.gen.json file 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

Project structure
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

Project structure
src/ mod_my_theme/
└── @alias/ui/jopijs.auth.loginForm/
    ├── high.priority        # Force this version
    └── index.tsx            # Your custom form component
src/mod_my_theme/@alias/ui/jopijs.auth.loginForm/index.tsx
export 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

src/mod_db_auth/@alias/lib/jopijs.auth.findUser/index.ts
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

src/mod_db_auth/@alias/lib/jopijs.auth.checkAuthData/index.ts
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:

IAuthData Structure
interface IAuthData {
    login: string;
    password: string;
}

D. Available Shared Items

CategoryShared PathPurposeBehavior
UI@/ui/jopijs.auth.loginFormThe actual login form.Replace
Assets@/res/jopijs.auth.imageThe side-image shown on the login page.Replace
Labels@/translations/page.loginAll labels and error messages.Merge
Core@/lib/jopijs.auth.findUserFetch a user from a DB.Replace
Core@/lib/jopijs.auth.checkAuthDataValidate 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 IUserInfos type as your application scales.

To learn more about checking roles and protecting specific routes, see the Limiting Access guide.