Authentication

XinAdmin uses Laravel Sanctum for token-based API authentication.

Authentication Overview

FeatureDescription
Auth DriverSanctum Personal Access Token
Token TransportAuthorization: Bearer {token} header
Token StorageFrontend localStorage
Token Expiry3 days default; permanent if "Remember me" is checked
Password Hashingbcrypt, 12 rounds (BCRYPT_ROUNDS=12)

Login Flow

User enters credentials → POST /system/login
  ├── Validate username/password (Auth::guard('sys_users')->attempt())
  ├── Retrieve user permission list (access() method)
  ├── Generate Sanctum Token (createToken)
  └── Return token + permissions array

Frontend receives response
  ├── localStorage.setItem('token', token)
  ├── Call GET /system/info to fetch user info + permissions
  └── Store in Zustand auth store

Token Verification Flow

Backend processing chain for each API request:

Request enters
  ├── AllowCrossDomainMiddleware (CORS)
  ├── LanguageMiddleware (language detection)
  ├── auth:sanctum          → Validates Bearer Token validity
  ├── authGuard             → Verifies token's tokenable_type matches user model
  └── abilities:{key}       → Verifies token's abilities contain required permission

Custom Token Model

XinAdmin extends Sanctum's PersonalAccessToken with key customizations:

  • Uses a dedicated table sys_access_token
  • Super admin (user ID = 1) always passes all ability checks
  • Supports wildcard permission * (for super admin role)
// SysAccessToken::can()
public function can($ability)
{
    // Super admin passes all checks
    if ($this->tokenable_id === 1) {
        return true;
    }
    // Check for * wildcard permission
    if (in_array('*', $this->abilities)) {
        return true;
    }
    return array_key_exists($ability, array_flip($this->abilities));
}

Login Auditing

LoginLogMiddleware records every login attempt to the sys_login_record table:

RecordDescription
User-AgentUser agent string
IP AddressLogin IP
Browser/OSParsed browser type and operating system
GeolocationQueried via ipinfo.io API
StatusSuccess / Failure

Frontend Login Protection

MechanismDescription
Entry GuardApp.tsx checks for token in localStorage on startup; redirects to /login if absent
401 HandlingAxios response interceptor catches 401 status, clears local token, and redirects to login
Token AttachmentStored in localStorage, auto-attached to Authorization header via Axios interceptor
LogoutCalls POST /system/logout then clears all local storage

Axios Interceptor

// Request interceptor: auto-attach token
instance.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  return config;
});

// Response interceptor: handle 401
instance.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

Password Security

ConfigDescription
Hash Algorithmbcrypt
RoundsBCRYPT_ROUNDS=12 (adjustable in .env)
Password RulesMinimum 6, maximum 20 characters; alphanumeric and hyphens only
Password ChangeRequires old password verification via password_verify() + Hash::make()