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
| Parameter | Type | Required | Default | Example | Impact |
|---|---|---|---|---|---|
email | string | ✅ | - | "[email protected]" | Primary authentication identifier |
password | string | ✅ | - | "SecurePassword123!" | User credentials for validation |
withUserData | boolean | ❌ | true | false | Controls user profile data retrieval |
withAccountData | boolean | ❌ | true | false | Controls account information inclusion |
useStore | boolean | ❌ | true | false | Controls 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 Code | Error Type | Cause | System Action | User Message |
|---|---|---|---|---|
| 400 | Bad Request | Invalid email format or missing fields | Show validation errors | "Please check your credentials" |
| 401 | Unauthorized | Invalid email/password combination | Clear any stored tokens | "Invalid email or password" |
| 403 | Forbidden | Account locked or disabled | Show account status | "Account access restricted" |
| 429 | Too Many Requests | Rate limiting triggered | Show retry timer | "Too many attempts, try later" |
| 500 | Internal Server Error | Server or database issues | Log 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
-
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
-
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
-
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
-
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
-
Session Cleanup
await logout(); // Clear any existing authentication state -
Parameter Validation
if (!email || !password) { throw new Error("Email and password are required"); } -
API Request
const { data, error } = await useFetch<LoginResponse>(url, { method: "POST", body: { email, password }, }); -
Token Storage
setTokenCookies(response.data.token); setCurrencyCookies(runtimeConfig.public.currency); -
Store Update
userStore.login({ user: response.data.userData.user, account: response.data.accountData, accounts: response.data.userData.accounts, });
Updated 5 months ago
