Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Step-by-Step Authentication Process

...

Step 1: User Visits Protected Page

What happens:

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

...

{
  "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., /overview or 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):

  1. User interacts with the app (e.g., viewing the overview page)
  2. Blazor app makes HTTP request to ApiService
  3. ApiAuthenticationHandler intercepts the request
  4. Handler asks MSAL.js for access token
  5. MSAL.js returns token from sessionStorage
  6. Handler adds token to request header: Authorization: Bearer eyJ0eXAi...
  7. 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):

  1. ApiService receives request with Bearer token
  2. JWT middleware extracts token from header
  3. Checks token signature (validates it's from Microsoft)
  4. Checks token issuer (must be CIAM tenant)
  5. Checks token audience (must be for this API)
  6. Checks token expiration (must not be expired)
  7. Checks user's groups claim (for authorization)
  8. If all valid → Allow request
  9. If any invalid → Return 401 Unauthorized

Who's involved:

  • ApiService
  • Microsoft Entra ID (signing keys downloaded periodically)

What's validated:

Result:

  • API processes request and returns data
  • Or returns 401/403 if unauthorized


...

Step 10: Token Renewal (Automatic)

What happens (when access token expires):

  1. Access token expires after 1 hour
  2. Next API call triggers token renewal
  3. MSAL.js uses refresh token to get new access token
  4. New access token stored in sessionStorage
  5. API call proceeds with new token
  6. 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


...

Token Details

Access Token (JWT)

Purpose: Proves user is authenticated and authorized for API calls Lifetime: 1 hour Contains:

  • User ID
  • User email
  • Azure AD groups (for authorization)
  • Expiration time
  • Issuer (Microsoft Entra ID)
  • Audience (ApiService)

Example (decoded):

{
  "iss": "https://6073ce8b-73f3-4df4-9b80-5e40cdc6965f.ciamlogin.com/.../v2.0",
  "aud": "api://4dad5d62-dc8c-4378-8bd0-ae736a4d73fe",
  "sub": "abc123...",
  "name": "John Doe",
  "email": "john.doe@contractor.com",
  "groups": ["ac6ec653-2ae3-457a-9302-d42429d83bee"],
  "exp": 1733754123
}

Refresh Token

Purpose: Get new access tokens without re-login Lifetime: 90 days Contains: Encrypted data (not readable) Note: Single-use (new refresh token issued with each renewal)

ID Token (JWT)

Purpose: User identity information for the frontend Lifetime: 1 hour Contains: Similar to access token but for frontend use Note: Not used for API authorization


...

Microsoft Graph API (Separate from Main Flow)

What Is It?

Microsoft Graph API is used for administrative and user management features - not for the main authentication flow described above.

When Is It Used?

1. Display User Information

  • Showing user's name in navigation bar
  • Displaying profile details
  • File: Components/Shared/NavBar.razor

2. Admin Features (Contractor/Grid Admins)

  • List all users in organization
  • View which groups a user belongs to
  • Invite new users to the portal
  • Add/remove users from groups
  • Files: ContractorAdmin.razor, GridAdmin.razor, EGAdmin.razor

Separate Token Required

Graph API requires its own access token, different from the API token:

TokenAudienceUsed For
API Tokenapi://4dad5d62-dc8c-4378-8bd0-ae736a4d73feCalling ApiService endpoints
Graph Tokenhttps://graph.microsoft.comUser management via Graph API

How It Works

  1. During login (Step 3), user consents to Graph API permission:

  2. When Graph API is needed, app requests Graph token:

    var result = await _tokenProvider.RequestAccessToken(
        new AccessTokenRequestOptions
        {
            Scopes = new[] { "https://graph.microsoft.com/User.Read" }
        });
    
  3. MSAL returns separate token from sessionStorage (or gets new one)

  4. GraphServiceClient uses this token to call Microsoft Graph API

Example: Get Current User

What happens:

  1. Admin opens their profile page
  2. Page calls: await GraphUserService.GetCurrentUserAsync()
  3. GraphServiceClient requests Graph token from MSAL
  4. MSAL returns token with audience https://graph.microsoft.com
  5. Request sent to Microsoft Graph API with Bearer token
  6. Graph API returns user details (name, email)

Key Points

  •  Independent of main authentication - Graph token is separate
  •  Not required for basic app use - Only for admin features
  •  Managed automatically by MSAL - App requests by scope
  •  Same user session - Part of same login, different API
  • ⚠️ Guest users - Limited to User.Read scope only

Files Involved

Service: Components/Authentication/AuthenticationService/GraphUserService.cs Configuration: Program.cs:104-142 Usage: Admin pages, NavBar, Profile page

...

Security Mechanisms

PKCE (Proof Key for Code Exchange)

What: Random secret generated by MSAL.js before login Why: Prevents authorization code theft How: Code can only be exchanged by app that started the flow

Token Signature Validation

What: Cryptographic signature on every JWT token Why: Proves token issued by Microsoft, not forged How: ApiService downloads Microsoft's public keys, validates signature

Token Expiration

What: Every token has expiration timestamp Why: Limits damage if token stolen How: ApiService rejects expired tokens automatically

sessionStorage (Not localStorage)

What: Browser storage that clears when tab closes Why: Reduces risk if user leaves computer unlocked How: MSAL.js configured to use sessionStorage