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