Authentication & Login
Complete guide to all authentication methods and login types available in the AquaGen Web Application.
Overview
AquaGen supports 5 different authentication methods to provide flexible and secure access:
- Username & Password (Default/Credentials Login)
- OTP (One-Time Password) via Mobile Number
- Microsoft Sign-In (Azure AD SSO)
- Google Sign-In (Google OAuth)
- Auto-Login (Session-based)
Key Features:
- Multi-provider authentication
- Single Sign-On (SSO) support
- Secure token-based authentication
- Session persistence
- Device fingerprinting
- Error handling and recovery
Authentication Architecture
Technology Stack
| Component | Technology | Purpose |
|---|---|---|
| Microsoft Auth | Azure MSAL 4.14.0 | Microsoft/Azure AD SSO |
| Google Auth | Google OAuth 0.12.2 | Google account login |
| JWT Tokens | JWT Decode 4.0.0 | Token validation |
| Session Storage | Browser API | Token persistence |
| Fingerprinting | FingerprintJS 5.0.1 | Device identification |
Configuration Files
Location: libs/shared/src/constants/authConfig.js
Key Configurations:
- Azure AD Client ID
- Google OAuth Client ID
- MSAL configuration
- Login scopes and permissions
Code Reference: libs/shared/src/constants/authConfig.js:1-74
Login Types
1. Username & Password Login
Default credential-based authentication.
How It Works
- User enters username and password
- Credentials validated against backend API
- JWT token issued on successful authentication
- User redirected to dashboard
Features
- Username validation: Minimum 4 characters, no spaces
- Password validation: Minimum 5 characters, no spaces
- Forgot password: Email support link
- Error handling: Invalid credential messages
- Loading states: Button spinner during authentication
User Interface
Location: libs/components/src/pages/login/DefaultLoginForm.jsx
Form Fields:
- Username input (max 25 characters)
- Password input (max 30 characters, hidden)
- "Forgot Password?" link
- "Log In" button
Validation Rules:
// Username validation
- Minimum length: 4 characters
- No spaces allowed
- Maximum length: 25 characters
// Password validation
- Minimum length: 5 characters
- No spaces allowed
- Maximum length: 30 characters
API Endpoint
Endpoint: POST /api/user/login
Request:
{
"username": "user@example.com",
"password": "securePassword123"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "refresh_token_string",
"loginData": {
"userId": "12345",
"name": "John Doe",
"email": "user@example.com",
"role": "admin"
},
"permissions": ["VIEW_DASHBOARD", "EDIT_SETTINGS"]
}
Code Example
import { LoginController } from '@aquagen-mf-webapp/components/pages/login/loginController';
// Login with credentials
const response = await LoginController.defaultLogin(username, password);
if (response) {
// Success - store token and redirect
LocalDBInstance.setItem(LocalDBKeys.loginResponse, JSON.stringify(response));
navigate('/dashboard');
} else {
// Failed - show error
setErrorMessage('Invalid Credentials');
}
Code Reference: libs/components/src/pages/login/DefaultLoginForm.jsx:47-111
2. OTP Login (Mobile Number)
One-Time Password authentication via SMS.
How It Works
- User enters registered mobile number
- OTP sent to mobile via SMS
- User enters 6-digit OTP
- OTP validated against backend
- JWT token issued on successful verification
- User redirected to dashboard
Features
- Mobile number validation: 10 digits, registered numbers only
- OTP generation: 6-digit numeric code
- Resend OTP: Available after 60 seconds
- Timer display: Countdown for resend availability
- Auto-enable login: Button enables when 6 digits entered
- OTP expiry: Time-limited validity
User Interface
Location: libs/components/src/pages/login/OtpLoginForm.jsx
Form Fields:
- Mobile number input (10 digits)
- "Send OTP" button (in-field)
- OTP input (6 digits)
- Resend OTP link (with timer)
- "Log In" button (enabled when OTP complete)
Flow States:
- Initial: Enter mobile number
- OTP Sent: Enter OTP code
- Resend Available: After 60 seconds
- Verification: Validating OTP
OTP Process
Step 1: Send OTP
Endpoint: POST /api/user/otp/send
Request:
{
"phoneNumber": "9876543210"
}
Response:
{
"status": "Success",
"message": "OTP sent successfully",
"otpTimestamp": "2024-02-25T10:30:00Z"
}
Step 2: Verify OTP
Endpoint: POST /api/user/otp/verify
Request:
{
"phoneNumber": "9876543210",
"otp": "123456",
"otpTimestamp": "2024-02-25T10:30:00Z"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "refresh_token_string",
"loginData": { ... }
}
Code Example
import { LoginController } from '@aquagen-mf-webapp/components/pages/login/loginController';
// Step 1: Send OTP
const sendResponse = await LoginController.otpPhnCheck(phoneNumber);
if (sendResponse?.status === 'Success') {
// OTP sent - show OTP input
setOtpTimestamp(sendResponse.otpTimestamp);
setShowOtpInput(true);
}
// Step 2: Verify OTP
const verifyResponse = await LoginController.otpLogin(
phoneNumber,
otpValue,
otpTimestamp
);
if (verifyResponse) {
// Success - store token and redirect
navigate('/dashboard');
}
Code Reference: libs/components/src/pages/login/OtpLoginForm.jsx:53-140
3. Microsoft Sign-In (Azure AD SSO)
Single Sign-On using Microsoft/Azure AD accounts.
How It Works
- User clicks "Microsoft" button
- Redirected to Microsoft login page
- User authenticates with Microsoft credentials
- Microsoft returns ID token
- Token sent to backend for validation
- Backend issues AquaGen JWT token
- User redirected to dashboard
Configuration
Azure AD Settings:
Location: libs/shared/src/constants/authConfig.js:14-50
export const msalConfig = {
auth: {
clientId: '30a53001-9e7e-4264-b685-b948a0036935',
authority: 'https://login.microsoftonline.com/organizations',
redirectUri: window.location.origin
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false
}
};
Login Scopes:
export const loginRequest = {
scopes: ['User.Read']
};
Features
- Organization support: Multi-tenant Azure AD
- Popup/Redirect flow: Redirect-based authentication
- Account selection: "select_account" prompt
- Token caching: Session storage
- Silent refresh: Background token renewal
- Logging: Configurable log levels
User Interface
Button: Microsoft logo with "Microsoft" text
Authentication Flow:
- Click Microsoft button
- Browser redirects to
login.microsoftonline.com - User signs in with Microsoft account
- Redirect back to AquaGen with token
- Auto-login processing
MSAL Integration
Library: @azure/msal-browser v4.14.0
Provider Setup:
Location: apps/production/src/bootstrap.jsx
import { MsalProvider } from '@azure/msal-react';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from '@aquagen-mf-webapp/shared/constants/authConfig';
// Create MSAL instance
const msalInstance = new PublicClientApplication(msalConfig);
// Initialize MSAL
await msalInstance.initialize();
// Wrap app with provider
<MsalProvider instance={msalInstance}>
<App />
</MsalProvider>
Code Example
import { useMsal } from '@azure/msal-react';
import { loginRequest } from '@aquagen-mf-webapp/shared/constants/authConfig';
const { instance } = useMsal();
// Initiate Microsoft login
const handleMicrosoftLogin = () => {
instance.loginRedirect({
...loginRequest,
prompt: 'select_account'
});
};
// Handle redirect response
const accounts = instance.getAllAccounts();
if (accounts.length > 0) {
const response = await LoginController.microsoftLogin(instance, accounts);
// Process response
}
Code Reference: libs/components/src/pages/login/login.jsx:33-44, 122-154
SSO Registration Check
If Microsoft account is not registered in AquaGen:
Error Response:
{
"error": "SSO_NOT_REGISTERED",
"email": "user@company.com",
"name": "John Doe",
"provider": "microsoft"
}
User sees error screen with registration instructions.
Code Reference: libs/components/src/pages/login/SSOErrorScreen.jsx
4. Google Sign-In (OAuth)
Single Sign-On using Google accounts.
How It Works
- User clicks "Google" button
- Redirected to Google OAuth page
- User authenticates with Google credentials
- Google returns ID token
- Token sent to backend for validation
- Backend issues AquaGen JWT token
- User redirected to dashboard
Configuration
Google OAuth Settings:
Location: libs/shared/src/constants/authConfig.js:71-73
export const googleConfig = {
clientId: '463665648594-npnkvu7092n4j585vjav0mmvr537h5hr.apps.googleusercontent.com'
};
Features
- OAuth 2.0 flow: Standard OAuth implementation
- OpenID Connect: Email and profile scopes
- Account selection: "select_account" prompt
- ID token validation: Server-side verification
- Nonce protection: CSRF protection
User Interface
Button: Google logo with "Google" text
Authentication Flow:
- Click Google button
- Browser redirects to
accounts.google.com - User signs in with Google account
- Redirect back to AquaGen with ID token (in URL hash)
- Auto-login processing
OAuth Flow Details
Authorization URL:
https://accounts.google.com/o/oauth2/v2/auth?
client_id=CLIENT_ID&
redirect_uri=REDIRECT_URI&
response_type=id_token&
scope=openid%20email%20profile&
nonce=RANDOM_NONCE&
prompt=select_account
Response:
https://app.aquagen.com/#id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6Ij...
Code Example
import { googleConfig } from '@aquagen-mf-webapp/shared/constants/authConfig';
// Initiate Google login
const handleGoogleLogin = () => {
const redirectUri = window.location.origin + window.location.pathname;
const scope = 'openid email profile';
const nonce = Math.random().toString(36).substring(7);
const googleAuthUrl =
`https://accounts.google.com/o/oauth2/v2/auth?` +
`client_id=${encodeURIComponent(googleConfig.clientId)}` +
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
`&response_type=id_token` +
`&scope=${encodeURIComponent(scope)}` +
`&nonce=${nonce}` +
`&prompt=select_account`;
window.location.href = googleAuthUrl;
};
// Handle redirect with token
const hash = window.location.hash;
if (hash && hash.includes('id_token=')) {
const params = new URLSearchParams(hash.substring(1));
const idToken = params.get('id_token');
const response = await LoginController.googleLogin({ credential: idToken });
// Process response
}
Code Reference: libs/components/src/pages/login/login.jsx:46-64, 90-120
SSO Registration Check
If Google account is not registered in AquaGen:
Error Response:
{
"error": "SSO_NOT_REGISTERED",
"email": "user@gmail.com",
"name": "John Doe",
"provider": "google"
}
User sees error screen with registration instructions.
5. Auto-Login (Session-based)
Automatic authentication using stored session.
How It Works
- User has previously logged in
- JWT token stored in session storage
- Token validated on app load
- If valid, user auto-logged in
- If expired, redirected to login
Features
- Persistent sessions: Across browser refreshes
- Token expiry check: Validates token freshness
- Silent refresh: Background token renewal
- Logout on expiry: Auto-logout when token expires
Session Storage
Storage Key: loginResponse
Stored Data:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "refresh_token_string",
"loginData": {
"userId": "12345",
"name": "John Doe",
"email": "user@example.com"
},
"permissions": [...]
}
Auto-Login Flow
Location: libs/components/src/pages/splash/splash.jsx
// Check for existing session
const existingLoginResponse = LocalDBInstance.getItem(LocalDBKeys.loginResponse);
if (existingLoginResponse) {
const loginData = JSON.parse(existingLoginResponse);
// Validate token
if (isTokenValid(loginData.token)) {
// Auto-login success
appStore.setLoginData(loginData);
navigate('/dashboard');
} else {
// Token expired - clear and show login
LocalDBInstance.removeItem(LocalDBKeys.loginResponse);
navigate('/login');
}
}
Login Screen
Layout
The login screen uses a split layout design:
Left Side (50%): Image/branding carousel Right Side (50%): Login forms
Location: libs/components/src/pages/login/login.jsx
Components
Image View
Component: ImageView
Purpose: Branding and marketing visuals
Location: libs/components/src/pages/login/ImageView.jsx
Login Form Toggle
Users can switch between:
- Credentials Login (Default)
- OTP Login
Toggle Button: "Use OTP" / "Use Credentials"
SSO Buttons
Google Button:
- Displayed if:
constants.googleSignInEnabled === true - Icon: Google logo
- Text: "Google"
Microsoft Button:
- Displayed if:
constants.microsoftSignInEnabled === true - Icon: Microsoft logo
- Text: "Microsoft"
Divider
Text: "Or continue with" Separates: Credential/OTP login from SSO buttons
Authentication Flow Diagrams
Complete Authentication Flow
SSO Registration Check Flow
SSO Login (Microsoft/Google)
│
▼
Get ID Token
│
▼
Send to Backend
│
├─── User Registered? ──► YES ──► Issue JWT Token ──► Dashboard
│
└─── NO ──► Return Error
│
▼
SSO Error Screen
│
├─► Show user details (name, email)
│
├─► Show registration instructions
│
└─► "Back to Login" button
Token Management
JWT Token Structure
Standard JWT format:
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"userId": "12345",
"email": "user@example.com",
"role": "admin",
"iat": 1708857600,
"exp": 1708944000
}
Token Storage
Location: Session Storage
Key: loginResponse
Lifetime: Until browser session ends or explicit logout
Token Refresh
Endpoint: POST /api/user/refresh
Request:
{
"refreshToken": "refresh_token_string"
}
Response:
{
"token": "new_jwt_token",
"refreshToken": "new_refresh_token"
}
Token Expiry Handling
Code Location: libs/shared/src/services/api/apiClient.js
Response Interceptor:
// Status 440 = Token Expired
if (response.status === 440) {
// Attempt token refresh
const refreshResult = await refreshToken();
if (refreshResult.success) {
// Retry original request with new token
return retryRequest(originalRequest);
} else {
// Refresh failed - redirect to login
navigate('/login');
}
}
Security Features
1. Device Fingerprinting
Library: FingerprintJS 5.0.1
Purpose: Unique device identification for security
Data Collected:
- Browser name and version
- Operating system
- Device type (desktop/mobile)
- Screen resolution
- Timezone
Usage: Sent with all API requests as headers
headers: {
'x-browser-name': 'Chrome',
'x-browser-version': '120.0.0',
'x-device-type': 'desktop',
'x-os': 'macOS',
'x-device-id': 'unique-fingerprint-hash'
}
2. CSRF Protection
OTP Login: Nonce generation Google OAuth: State parameter and nonce Microsoft MSAL: Built-in CSRF protection
3. XSS Protection
Content Security Policy: Strict CSP headers Input Sanitization: All inputs validated Token Storage: HttpOnly cookies (for refresh tokens)
4. Password Security
Client-side:
- No plain-text storage
- Cleared after login attempt
Server-side:
- Bcrypt hashing
- Salt per password
- Rate limiting on login attempts
Login Analytics
All login events are tracked via Analytics Service:
Events Tracked:
MICROSOFT_SSO_BUTTON_CLICKGOOGLE_SSO_BUTTON_CLICKDEFAULT_LOGIN_CLICKDEFAULT_LOGIN_ERRORSEND_OTP_CLICKRESEND_OTPOTP_LOGIN_CLICKOTP_LOGIN_ERROR
Code Reference: libs/components/src/pages/login/login.jsx:34, 47
Error Handling
Common Errors
1. Invalid Credentials
Message: "Invalid Credentials" Cause: Wrong username/password Solution: Re-enter correct credentials
2. Number Not Registered
Message: "Number Not Registered" Cause: Mobile number not in system Solution: Contact admin to register number
3. Invalid OTP
Message: "Invalid OTP" Cause: Wrong OTP or expired OTP Solution: Request new OTP
4. SSO Not Registered
Message: Custom screen with user details Cause: SSO email not registered in AquaGen Solution: Contact admin to register email
5. Network Error
Message: "Network error occurred" Cause: API unavailable or network issue Solution: Check connection and retry
Error Display
Username/Password Errors:
- Red border on input fields
- Error message below fields
OTP Errors:
- Red error text
- Field validation indicators
SSO Errors:
- Full-screen error page
- User details displayed
- "Back to Login" option
Configuration
Enable/Disable Login Methods
Location: libs/shared/src/constants/constants.js
export const constants = {
// Enable/disable Google SSO
googleSignInEnabled: true,
// Enable/disable Microsoft SSO
microsoftSignInEnabled: true,
// OTP resend timer (seconds)
resendOtpTime: 60
};
Environment-Specific URLs
Location: libs/shared/src/services/api/urls.js
export class Urls {
// Azure AD redirect URL (per environment)
static azureADRedirectUrl = window.location.origin;
// API base URL
static baseURL = 'https://prod-aquagen.azurewebsites.net/api/user';
}
API Endpoints Summary
| Method | Endpoint | Purpose | Login Type |
|---|---|---|---|
| POST | /api/user/login | Credentials login | Username/Password |
| POST | /api/user/otp/send | Send OTP | OTP |
| POST | /api/user/otp/verify | Verify OTP | OTP |
| POST | /api/user/sso/microsoft | Microsoft SSO | Microsoft |
| POST | /api/user/sso/google | Google SSO | |
| POST | /api/user/refresh | Refresh token | All |
| POST | /api/user/logout | Logout | All |
Code References
Key Files
| File | Purpose | Location |
|---|---|---|
| authConfig.js | Auth configuration | libs/shared/src/constants/authConfig.js |
| login.jsx | Main login screen | libs/components/src/pages/login/login.jsx |
| DefaultLoginForm.jsx | Credentials form | libs/components/src/pages/login/DefaultLoginForm.jsx |
| OtpLoginForm.jsx | OTP form | libs/components/src/pages/login/OtpLoginForm.jsx |
| loginController.js | Login logic | libs/components/src/pages/login/loginController.js |
| loginDataSource.js | API calls | libs/components/src/pages/login/loginDataSource.js |
| SSOErrorScreen.jsx | SSO error page | libs/components/src/pages/login/SSOErrorScreen.jsx |
| AppStore.js | Auth state | libs/shared/src/store/AppStore.js |
| apiClient.js | Token interceptor | libs/shared/src/services/api/apiClient.js |
Testing Authentication
Test Credentials (Development)
Username/Password:
Username: test@aquagen.com
Password: TestPassword123
OTP:
Mobile: 9999999999
OTP: Any 6 digits (dev mode)
Testing SSO
Microsoft:
- Use personal/work Microsoft account
- Ensure account registered in backend
- Test account selection flow
Google:
- Use personal Google account
- Ensure account registered in backend
- Test OAuth redirect flow
Troubleshooting
Issue: SSO Redirect Loop
Cause: Token not clearing after failed SSO Solution: Clear session storage and retry
sessionStorage.clear();
window.location.reload();
Issue: OTP Not Received
Cause: SMS service delay or number not registered Solution:
- Wait 60 seconds
- Click "Resend OTP"
- Check mobile number is correct
Issue: Microsoft Login Stuck
Cause: MSAL cache issue Solution: Clear MSAL cache
instance.clearCache();
Issue: Auto-Login Not Working
Cause: Token expired or cleared
Solution: Check session storage for loginResponse key
Best Practices
1. Always Validate Tokens
// Check token before using
const token = getStoredToken();
if (!isTokenExpired(token)) {
// Use token
} else {
// Refresh or re-login
}
2. Handle SSO Errors Gracefully
// Always check for SSO registration
if (result?.error === 'SSO_NOT_REGISTERED') {
// Show friendly error screen
showSSOErrorScreen(result.email, result.provider);
}
3. Clear Sensitive Data
// Clear password after use
setPassword('');
// Clear OTP after verification
setOtp('');
4. Log Authentication Events
// Track successful login
AnalyticsService.sendEvent(AnalyticEvents.LOGIN_SUCCESS, {
method: 'microsoft',
timestamp: Date.now()
});
Next Steps
- Review Routes Documentation for protected route configuration
- Check Deployment Guide for authentication in production
- See API Documentation for token usage
Last Updated: February 2026 Maintained By: AquaGen Development Team