Login

Header

  • Composable Action: composables/useDjustAuth/useAuthApi.ts
  • API Route: POST /api/auth/login

Detailed Description

The login action is the core authentication feature that enables users to securely access the application. It plays a central role in the authentication system by providing comprehensive user credential validation, automatic token management, and seamless user state initialization.

This action handles multiple aspects of the authentication process including credential validation, JWT token generation and storage, user data retrieval, account selection for multi-account users, and automatic session initialization. The function is designed to be secure, efficient, and user-friendly while maintaining strict security standards.

The login function is used throughout the application in various contexts including user login forms, authentication guards, session restoration, and automated login processes. It serves as the primary entry point for user authentication and establishes the foundation for all subsequent authenticated operations.

Process Flow

sequenceDiagram
    participant U as User
    participant C as Vue Component
    participant UA as useDjustAuth
    participant S as User Store
    participant API as Auth API
    participant Cookies as Cookie Storage

    U->>C: Submit credentials
    C->>UA: login(email, password, options)
    UA->>UA: Clear previous session
    UA->>API: POST /auth/login
    API->>UA: JWT tokens + user data
    UA->>Cookies: Store access & refresh tokens
    UA->>S: Update user state
    S->>C: Authentication success
    C->>U: Redirect to dashboard

    alt Invalid Credentials
        API-->>UA: 401 Unauthorized
        UA-->>C: Authentication failed
        C-->>U: Show error message
    else Network Error
        API-->>UA: 500 Server Error
        UA-->>C: Connection error
        C-->>U: Show network error
    end

Call Parameters

Main Interface

interface LoginRequest {
  email: string; // User email address
  password: string; // User password
  withUserData?: boolean; // Include user profile data (default: true)
  withAccountData?: boolean; // Include account details (default: true)
}

Detailed Parameters

ParameterTypeRequiredDefaultExampleImpact
emailstring-"[email protected]"Primary authentication identifier
passwordstring-"SecurePassword123!"User credentials for validation
withUserDatabooleantruefalseControls user profile data retrieval
withAccountDatabooleantruefalseControls account information inclusion
useStorebooleantruefalseControls automatic store state updates

Example Call

const { login } = useDjustAuth();

try {
  const success = await login({
    email: "[email protected]",
    password: "MySecurePassword123!",
    withUserData: true,
    withAccountData: true,
  });

  if (success) {
    console.log("Login successful");
    // Redirect to dashboard or previous page
  }
} catch (error) {
  console.error("Login failed:", error.message);
}

Returns from Composable

Success Response Format

interface LoginResponse extends AuthenticationResponseDto {
  success: boolean;
  data: LoginResponseData;
}

interface LoginResponseData extends AuthenticationResponseDto {
  token: Token;
  user: CustomerUser;
  userData?: {
    user: CustomerUser;
    account: CustomerAccountNameDto;
    accounts: CustomerAccountNameDto[];
  };
  accountData?: GetCustomerAccountResponse;
}

interface Token {
  accessToken: string;
  refreshToken: string;
  expireAt: number;
}

Example Success Response

{
  "success": true,
  "data": {
    "token": {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "expireAt": 1640995200
    },
    "user": {
      "id": "user_123",
      "email": "[email protected]",
      "firstName": "John",
      "lastName": "Doe"
    },
    "userData": {
      "user": {
        "id": "user_123",
        "email": "[email protected]",
        "firstName": "John",
        "lastName": "Doe",
        "preferences": {}
      },
      "account": {
        "id": "account_456",
        "name": "Company Ltd",
        "externalId": "COMP001"
      },
      "accounts": [
        {
          "id": "account_456",
          "name": "Company Ltd",
          "externalId": "COMP001"
        }
      ]
    },
    "accountData": {
      "id": "account_456",
      "name": "Company Ltd",
      "externalId": "COMP001",
      "settings": {},
      "permissions": []
    }
  }
}

Error Management

Error Types and Returns

HTTP CodeError TypeCauseSystem ActionUser Message
400Bad RequestInvalid email format or missing fieldsShow validation errors"Please check your credentials"
401UnauthorizedInvalid email/password combinationClear any stored tokens"Invalid email or password"
403ForbiddenAccount locked or disabledShow account status"Account access restricted"
429Too Many RequestsRate limiting triggeredShow retry timer"Too many attempts, try later"
500Internal Server ErrorServer or database issuesLog error, show generic"Service temporarily unavailable"

Error Handling Code Example

const { login } = useDjustAuth();
const { $toast } = useNuxtApp();

try {
  const success = await login({
    email: credentials.email,
    password: credentials.password,
  });

  if (success) {
    await navigateTo("/dashboard");
  }
} catch (error) {
  switch (error.statusCode) {
    case 400:
      $toast.error("Please check your credentials format");
      break;
    case 401:
      $toast.error("Invalid email or password");
      break;
    case 403:
      $toast.error("Your account access is restricted");
      break;
    case 429:
      $toast.error("Too many login attempts. Please try again later");
      break;
    default:
      $toast.error("Login service is temporarily unavailable");
  }

  console.error("Login error:", error);
}

Use Cases

  1. Standard User Login

    • User enters email and password on login form
    • System validates credentials and establishes session
    • User is redirected to dashboard or intended page
    • All user data and preferences are loaded
  2. Multi-Account User Authentication

    • User with access to multiple company accounts logs in
    • System provides account selection interface
    • User can switch between accounts without re-authentication
    • Account-specific permissions and data are loaded
  3. Session Recovery

    • User returns to application with expired session
    • System prompts for re-authentication
    • Previous application state is restored after login
    • User continues from where they left off
  4. Programmatic Authentication

    • System performs automated login for service accounts
    • API integrations authenticate without user interaction
    • Background processes establish authenticated sessions
    • System-to-system authentication workflows

Important Points

Performance

The login process is optimized for speed and efficiency with minimal API calls and intelligent data loading. The system uses configurable data fetching options to load only necessary information, implements efficient token storage mechanisms, and provides optimized state management through Pinia store integration.

Security

Security is paramount with multiple layers of protection including encrypted password transmission over HTTPS, secure JWT token generation with proper expiration, HTTP-only cookie storage to prevent XSS attacks, and automatic session cleanup on authentication failures.

Flexibility

The system supports various authentication scenarios with configurable user data loading, optional account information fetching, support for both email and username authentication, and extensible error handling for custom authentication flows.

Integration

Deep integration with the application ecosystem includes automatic Pinia store updates, seamless cookie management, integration with route protection middleware, and comprehensive error handling with toast notifications.

Technical Implementation

/**
 * Authenticates a user with email and password
 * @param params - Login parameters
 * @param params.email - User email address
 * @param params.password - User password
 * @param params.withUserData - Include user profile data
 * @param params.withAccountData - Include account details
 * @param useStore - Whether to update the Pinia store
 * @returns Promise<boolean | undefined> - Success status
 * @throws {Error} - Authentication or network errors
 */
const login = async (
  {
    email,
    password,
    withUserData = true,
    withAccountData = true,
  }: LoginRequest,
  useStore = true
): Promise<boolean | undefined> => {
  const userStore = useUserStore();
  const runtimeConfig = useRuntimeConfig();

  // Clear any existing session
  await logout();

  try {
    // Build query parameters
    const params = new URLSearchParams();
    if (withUserData !== undefined) {
      params.append("withUserData", String(withUserData));
    }
    if (withAccountData !== undefined) {
      params.append("withAccountData", String(withAccountData));
    }

    const url = `/api/auth/login?${params.toString()}`;

    // Make authenticated API call
    const { data, error } = await useFetch<LoginResponse>(url, {
      method: "POST",
      body: {
        email: email,
        password: password,
      },
    });

    if (error.value) {
      throw new Error(error.value.data.message || "Login failed.");
    }

    const response: LoginResponse | null = data.value;
    if (response?.success) {
      // Store authentication tokens securely
      setTokenCookies(response.data.token);
      setCurrencyCookies(runtimeConfig.public.currency);

      // Update user store with authentication data
      if (useStore) {
        userStore.login({
          user: response.data.userData
            ? response.data.userData.user
            : response.user,
          account: response.data.accountData ? response.data.accountData : {},
          accounts: response.data.userData
            ? response.data.userData.accounts
            : [],
          currency: runtimeConfig.public.currency,
        });
      }

      return response.success;
    }
  } catch (error) {
    console.error("Failed to login:", error);
    throw new Error((error as string) || "Login failed.");
  }
};

Execution Flow

  1. Session Cleanup

    await logout(); // Clear any existing authentication state
  2. Parameter Validation

    if (!email || !password) {
      throw new Error("Email and password are required");
    }
  3. API Request

    const { data, error } = await useFetch<LoginResponse>(url, {
      method: "POST",
      body: { email, password },
    });
  4. Token Storage

    setTokenCookies(response.data.token);
    setCurrencyCookies(runtimeConfig.public.currency);
  5. Store Update

    userStore.login({
      user: response.data.userData.user,
      account: response.data.accountData,
      accounts: response.data.userData.accounts,
    });