Versions Compared

Key

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

...

Step 1: User Visits Protected Page

What happens:

...

  • 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