Skip to main content

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:

  1. Username & Password (Default/Credentials Login)
  2. OTP (One-Time Password) via Mobile Number
  3. Microsoft Sign-In (Azure AD SSO)
  4. Google Sign-In (Google OAuth)
  5. 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​

ComponentTechnologyPurpose
Microsoft AuthAzure MSAL 4.14.0Microsoft/Azure AD SSO
Google AuthGoogle OAuth 0.12.2Google account login
JWT TokensJWT Decode 4.0.0Token validation
Session StorageBrowser APIToken persistence
FingerprintingFingerprintJS 5.0.1Device 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​

  1. User enters username and password
  2. Credentials validated against backend API
  3. JWT token issued on successful authentication
  4. 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​

  1. User enters registered mobile number
  2. OTP sent to mobile via SMS
  3. User enters 6-digit OTP
  4. OTP validated against backend
  5. JWT token issued on successful verification
  6. 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:

  1. Initial: Enter mobile number
  2. OTP Sent: Enter OTP code
  3. Resend Available: After 60 seconds
  4. 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​

  1. User clicks "Microsoft" button
  2. Redirected to Microsoft login page
  3. User authenticates with Microsoft credentials
  4. Microsoft returns ID token
  5. Token sent to backend for validation
  6. Backend issues AquaGen JWT token
  7. 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:

  1. Click Microsoft button
  2. Browser redirects to login.microsoftonline.com
  3. User signs in with Microsoft account
  4. Redirect back to AquaGen with token
  5. 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​

  1. User clicks "Google" button
  2. Redirected to Google OAuth page
  3. User authenticates with Google credentials
  4. Google returns ID token
  5. Token sent to backend for validation
  6. Backend issues AquaGen JWT token
  7. 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:

  1. Click Google button
  2. Browser redirects to accounts.google.com
  3. User signs in with Google account
  4. Redirect back to AquaGen with ID token (in URL hash)
  5. 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​

  1. User has previously logged in
  2. JWT token stored in session storage
  3. Token validated on app load
  4. If valid, user auto-logged in
  5. 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_CLICK
  • GOOGLE_SSO_BUTTON_CLICK
  • DEFAULT_LOGIN_CLICK
  • DEFAULT_LOGIN_ERROR
  • SEND_OTP_CLICK
  • RESEND_OTP
  • OTP_LOGIN_CLICK
  • OTP_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​

MethodEndpointPurposeLogin Type
POST/api/user/loginCredentials loginUsername/Password
POST/api/user/otp/sendSend OTPOTP
POST/api/user/otp/verifyVerify OTPOTP
POST/api/user/sso/microsoftMicrosoft SSOMicrosoft
POST/api/user/sso/googleGoogle SSOGoogle
POST/api/user/refreshRefresh tokenAll
POST/api/user/logoutLogoutAll

Code References​

Key Files​

FilePurposeLocation
authConfig.jsAuth configurationlibs/shared/src/constants/authConfig.js
login.jsxMain login screenlibs/components/src/pages/login/login.jsx
DefaultLoginForm.jsxCredentials formlibs/components/src/pages/login/DefaultLoginForm.jsx
OtpLoginForm.jsxOTP formlibs/components/src/pages/login/OtpLoginForm.jsx
loginController.jsLogin logiclibs/components/src/pages/login/loginController.js
loginDataSource.jsAPI callslibs/components/src/pages/login/loginDataSource.js
SSOErrorScreen.jsxSSO error pagelibs/components/src/pages/login/SSOErrorScreen.jsx
AppStore.jsAuth statelibs/shared/src/store/AppStore.js
apiClient.jsToken interceptorlibs/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:

  1. Use personal/work Microsoft account
  2. Ensure account registered in backend
  3. Test account selection flow

Google:

  1. Use personal Google account
  2. Ensure account registered in backend
  3. 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:

  1. Wait 60 seconds
  2. Click "Resend OTP"
  3. 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​


Last Updated: February 2026 Maintained By: AquaGen Development Team