Step-by-Step Process & Entities
Entities Involved
1. End User (Browser)
- The person accessing the Partner Portal
- Can be a contractor employee or grid company employee
- Uses their email and password to log in
2. Blazor WebAssembly Application (Frontend)
- The Partner Portal web interface running in the user's browser
- Built with .NET Blazor WebAssembly
- Location:
Source/EGU.PartnerPortal.Web - Runs entirely client-side (in the browser)
3. MSAL.js Library
- Microsoft Authentication Library for JavaScript
- Embedded in the Blazor app
- Handles all OAuth2/OpenID Connect communication
- Manages tokens and session state
4. Microsoft Entra ID (CIAM Tenant)
- Microsoft's cloud identity service
- Stores user credentials and profiles
- Tenant ID:
6073ce8b-73f3-4df4-9b80-5e40cdc6965f - Issues JWT tokens after successful authentication
- Hosted by Microsoft (external service)
5. ApiService (Backend API)
- The Partner Portal backend REST API
- Built with ASP.NET Core
- Location:
Source/EGU.PartnerPortal.ApiService - Validates JWT tokens on every API request
- Enforces authorization policies
High-Level Authentication Flows
Flow 1: User Login & Authentication (Steps 1-7)
┌─────────────────────────────────────────────────────────────────┐
│ LOGIN & AUTHENTICATION FLOW (Steps 1-7) │
└─────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ End User │
│ Browser │
└──────┬───────┘
│
│ Step 1: Navigate to app
│ (https://partners-dev.test-egzynergy.com/)
▼
┌──────────────────────┐
│ Blazor WebAssembly │
│ (Partner Portal) │
└──────┬───────────────┘
│
│ Step 2: Check sessionStorage
│ No valid token found
│ → Redirect to login
▼
┌──────────────────────────────┐
│ Microsoft Entra ID (CIAM) │
│ Login Page │
└──────┬───────────────────────┘
│
│ Step 3: User enters
│ email + password + MFA
▼
┌──────────────────────────────┐
│ Microsoft Entra ID │
│ Validates Credentials │
└──────┬───────────────────────┘
│
│ ✅ Valid credentials
│
│ Step 4: Returns
│ authorization code
▼
┌──────────────────────┐
│ MSAL.js Library │
│ (in browser) │
└──────┬───────────────┘
│
│ Step 5: Exchange code
│ for tokens (PKCE)
▼
┌──────────────────────────────┐
│ Microsoft Entra ID │
│ Token Endpoint │
└──────┬───────────────────────┘
│
│ Returns 3 tokens:
│ • Access Token (1h)
│ • Refresh Token (90d)
│ • ID Token (1h)
▼
┌──────────────────────┐
│ Browser │
│ sessionStorage │
└──────┬───────────────┘
│
│ Step 6: Tokens stored
│ (indexed by scope)
│
│ Step 7: Redirect back
│ to original page
▼
┌────────────────────────────────┐
│ ✅ USER AUTHENTICATED │
│ Ready to use the app │
└────────────────────────────────┘
Flow 2: API Calls with Token Validation (Steps 8-9)
┌─────────────────────────────────────────────────────────────────┐
│ API CALL & VALIDATION FLOW (Steps 8-9) │
└─────────────────────────────────────────────────────────────────┘
┌────────────────────────────────┐
│ User interacts with app │
│ (e.g., views overview page) │
└────────────┬───────────────────┘
│
│ Step 8: Blazor makes API request
▼
┌──────────────────────────────────┐
│ ApiAuthenticationHandler │
│ (HTTP Message Handler) │
└──────┬───────────────────────────┘
│
│ Requests token from MSAL.js
│ Scope: api://4dad5d62.../ApiService_UserAccess
▼
┌──────────────────────┐
│ MSAL.js Library │
│ sessionStorage │
└──────┬───────────────┘
│
│ Returns access token
│ (or refreshes if expired)
▼
┌──────────────────────────────┐
│ HTTP Request │
│ GET /api/v1/WorkOrder/... │
│ Authorization: Bearer eyJ... │
└──────┬───────────────────────┘
│
│ Step 9: Request received
▼
┌──────────────────────────────────┐
│ ApiService │
│ JWT Authentication Middleware │
└──────┬───────────────────────────┘
│
│ Token Validation:
│ ✓ Signature (Azure AD keys)
│ ✓ Issuer (CIAM tenant)
│ ✓ Audience (this API)
│ ✓ Expiration (not expired)
│ ✓ Groups (authorization)
│
├─── ✅ Valid ────┐ ❌ Invalid ───┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Controller │ │ 200 OK │ │ 401/403 │
│ Processes │ │ Returns │ │ Unauthorized│
│ Request │ │ Data │ │ Forbidden │
└──────────────┘ └──────────────┘ └──────────────┘
┌───────────────────────────────────────────────────────────────┐
│ Step 10: TOKEN RENEWAL (automatic, happens in background) │
│ │
│ When access token expires (after 1 hour): │
│ 1. MSAL.js detects expired token │
│ 2. Uses refresh token to get new access token │
│ 3. Stores new access token in sessionStorage │
│ 4. User continues working (no interruption) │
└───────────────────────────────────────────────────────────────┘
Step-by-Step Authentication Process
Step 1: User Visits Protected Page
What happens:
- User navigates to
https://partners-dev.test-egzynergy.com/(or/overview) - Page has
[Authorize]attribute, requires authentication
Who's involved:
- End User (Browser)
- Blazor WebAssembly App
Result:
- Blazor app checks if user has a valid token in sessionStorage
- No token found → Proceed to Step 2
Step 2: Redirect to Login
What happens:
- MSAL.js detects no authentication
- Saves the original URL (e.g.,
/overview) to return later - Redirects browser to Microsoft login page
Who's involved:
- MSAL.js Library
- Microsoft Entra ID
Redirect URL:
https://6073ce8b-73f3-4df4-9b80-5e40cdc6965f.ciamlogin.com/.../authorize
?client_id=84c38b43-12e4-4c26-8292-8910d79aa532
&redirect_uri=https://partners.egzynergy.com/authentication/login-callback
&response_type=code
Result:
- User sees Microsoft login page
Step 3: User Enters Credentials
What happens:
- User enters email (e.g.,
john.doe@contractor.com) - User enters password
- Completes MFA challenge via Microsoft Authenticator app
- (First time only) User consents to app permissions
Who's involved:
- End User
- Microsoft Entra ID
Result:
- Microsoft Entra ID validates credentials
- If valid → Proceed to Step 4
- If invalid → Show error, retry
Step 4: Authorization Code Issued
What happens:
- Microsoft Entra ID generates a one-time authorization code
- Redirects browser back to the app with the code
Who's involved:
- Microsoft Entra ID
- Blazor WebAssembly App
Redirect back:
https://partners.egzynergy.com/authentication/login-callback
?code=0.AXAA-very-long-code-here
&state=random-state-value
Result:
- App receives authorization code (valid for 10 minutes)
Step 5: Exchange Code for Tokens
What happens:
- MSAL.js automatically exchanges the code for tokens
- Sends code + PKCE verifier to Microsoft token endpoint
- Microsoft validates the code and issues tokens
Who's involved:
- MSAL.js Library
- Microsoft Entra ID
Request to Microsoft:
POST https://.../oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
code=0.AXAA-very-long-code-here
client_id=84c38b43-12e4-4c26-8292-8910d79aa532
redirect_uri=https://partners.egzynergy.com/authentication/login-callback
code_verifier=PKCE-verifier
Response from Microsoft:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"refresh_token": "0.AXAA...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"expires_in": 3600
}
Result:
- App now has three tokens (access, refresh, ID)
Step 6: Store Tokens
What happens:
- MSAL.js stores all tokens in browser's sessionStorage
- Tokens are encrypted/encoded but not visible to user
- sessionStorage means tokens deleted when browser tab closes
Who's involved:
- MSAL.js Library
- Browser sessionStorage
What's stored:
- Access Token: Used to call ApiService endpoints (1 hour lifetime)
- Refresh Token: Used to get new access tokens (90 days lifetime)
- ID Token: Contains user info (name, email, groups)
Result:
- User is now authenticated
- Tokens ready for API calls
Step 7: Redirect to Original Page
What happens:
- MSAL.js redirects user back to original page they requested
- User sees the page they originally wanted (e.g.,
/overviewor root/)
Who's involved:
- MSAL.js Library
- Blazor WebAssembly App
Result:
- User successfully logged in and viewing protected content
Step 8: Making API Calls
What happens (every time app calls API):
- User interacts with the app (e.g., viewing the overview page)
- Blazor app makes HTTP request to ApiService
ApiAuthenticationHandlerintercepts the request- Handler asks MSAL.js for access token
- MSAL.js returns token from sessionStorage
- Handler adds token to request header:
Authorization: Bearer eyJ0eXAi... - Request sent to ApiService
Who's involved:
- Blazor WebAssembly App
- ApiAuthenticationHandler
- MSAL.js Library
- ApiService
Result:
- API request includes authentication proof
Step 9: API Validates Token
What happens (on ApiService):
- ApiService receives request with Bearer token
- JWT middleware extracts token from header
- Checks token signature (validates it's from Microsoft)
- Checks token issuer (must be CIAM tenant)
- Checks token audience (must be for this API)
- Checks token expiration (must not be expired)
- Checks user's groups claim (for authorization)
- If all valid → Allow request
- If any invalid → Return 401 Unauthorized
Who's involved:
- ApiService
- Microsoft Entra ID (signing keys downloaded periodically)
What's validated:
- Signature: Proves token issued by Microsoft, not forged
- Issuer:
https://6073ce8b-73f3-4df4-9b80-5e40cdc6965f.ciamlogin.com/.../v2.0 - Audience:
api://4dad5d62-dc8c-4378-8bd0-ae736a4d73fe - Expiration: Token must not be expired
- Groups: User must be in authorized groups
Result:
- API processes request and returns data
- Or returns 401/403 if unauthorized
Step 10: Token Renewal (Automatic)
What happens (when access token expires):
- Access token expires after 1 hour
- Next API call triggers token renewal
- MSAL.js uses refresh token to get new access token
- New access token stored in sessionStorage
- API call proceeds with new token
- User doesn't notice anything (seamless)
Who's involved:
- MSAL.js Library
- Microsoft Entra ID
When user must log in again:
- Refresh token expires (after 90 days)
- User closes browser tab (sessionStorage cleared)
- User clicks "Logout"
- Admin revokes user's access in Azure AD