Blazor Apps: The Login Display – Part 2

This is part two of two, on Blazor Apps and the Login Display.

In my first post I discussed some basics around the LoginDisplay Razor component in Blazor server-side applications. In this post I will expand out the sample, to link the user display name value to navigate to another Razor component as a page to call MS Graph and return a basic set of user properties.

Updating your Blazor App Dependencies

You will need to update your application dependencies and introduce a new service, in this case a TokenProvider, to return the access token and refresh token, with additional scopes to call MS Graph. For simplicity of this article, this is well defined in the following article already by Microsoft.

ASP.NET Core Blazor Server additional security scenarios

MS Graph Client

Whilst you can build your own HTTP client to call the MS Graph REST APIs, Microsoft provide the Graph Client library for .Net, so in my sample I installed the nuget package in my project.

Azure AD Permissions

I already have the Azure AD permissions in place for the application to read user profiles in the tenant, but I’ve provided a sample below.

New UserProfile.cs Class

A new class is created for each profile property I was interested in displaying on the profile page. The code for the class is below. For simplicity, the types are all strings.

namespace BlazorProfile.Data
{
    public class UserProfile
    {
        public string ObjectId { get; set; }
        public string DisplayName { get; set; }
        public string Office { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string UPN  { get; set; }
        public string JobTitle { get; set; }
        public string Department { get; set; }
        public string MobilePhone { get; set; }
        public string CompanyName { get; set; }
        public string EmployeeId { get; set; }
    }
}

New LCProfile Component

I then created a new Razor page “LCProfile” to host the code required to utilise the TokenProvider service. The code for the component is below.

@page "/lcprofile"
@using BlazorProfile.Data
@inject AuthenticationStateProvider AuthenticationStateProvider
@using Microsoft.Identity.Client
@inject Microsoft.Extensions.Configuration.IConfiguration config
@using Microsoft.Graph
@using Microsoft.AspNetCore.Components.Authorization
@using System.Net.Http.Headers
@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor httpContextAccessor
@using BlazorProfile.Auth
@inject TokenProvider TokenProvider

<AuthorizeView>
    <Authorized>
        <h3>Profile</h3>
        <table class="table" width="500px">
            <tr>
                <td>First Name: @prof.FirstName</td>
            </tr>
            <tr>
                <td>Last Name: @prof.LastName</td>
            </tr>
            <tr>
                <td>ObjectId: @prof.ObjectId</td>
            </tr>
            <tr>
                <td>Display Name: @prof.DisplayName</td>
            </tr>
            <tr>
                <td>Company: @prof.CompanyName</td>
            </tr>
            <tr>
                <td>Job Title: @prof.JobTitle</td>
            </tr>
            <tr>
                <td>Department: @prof.Department</td>
            </tr>
            <tr>
                <td>Office: @prof.Office</td>
            </tr>
            <tr>
                <td>UPN: @prof.UPN</td>
            </tr>
            <tr>
                <td>Office Phone: @prof.MobilePhone</td>
            </tr>
        </table>
@ErrorMessage
</Authorized>
        <NotAuthorized>
            You have not authenticated!
        </NotAuthorized>
    </AuthorizeView>

@code {

        private string ErrorMessage;

        private UserProfile prof = new UserProfile();

        protected override async Task OnInitializedAsync()
        {
                ErrorMessage = "No exceptions on page load";
            try
            {

                var accesstoken = TokenProvider.AccessToken;

                var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
                var authenticateduser = authState.User.Claims.First(c => c.Type == "preferred_username");




                var graphClient = new GraphServiceClient(
                new DelegateAuthenticationProvider(requestMessage =>
                {
                    requestMessage.Headers.Authorization =
                        new AuthenticationHeaderValue("bearer", accesstoken);
                    return Task.FromResult(0);
                }));


                User graphuser = await graphClient.Users[authenticateduser.Value]
                   .Request()
                   .Select(u => new
                   {
                       u.GivenName,
                       u.Surname,
                       u.Id,
                       u.UserPrincipalName,
                       u.OfficeLocation,
                       u.Department,
                       u.JobTitle,
                       u.MobilePhone,
                       u.DisplayName,
                       u.EmployeeId,
                       u.CompanyName,
                       u.City
                   })
           .GetAsync();

                prof.FirstName = graphuser.GivenName;
                prof.LastName = graphuser.Surname;
                prof.ObjectId = graphuser.Id;
                prof.UPN = graphuser.UserPrincipalName;
                prof.Office = graphuser.OfficeLocation;
                prof.Department = graphuser.Department;
                prof.JobTitle = graphuser.JobTitle;
                prof.MobilePhone = graphuser.MobilePhone;
                prof.DisplayName = graphuser.DisplayName;
                prof.EmployeeId = graphuser.EmployeeId;
                prof.CompanyName = graphuser.CompanyName;

            }
            catch (Exception e)
            {
                //throw new Exception (e.Message);
                ErrorMessage = e.Message;
            }
        }
    }

The TokenProvider is injected into the component and then referenced in the GraphClient. I also use the AuthenticationStateProvider to pull back the users principal name, in order to query MS Graph for the users Azure AD profile data.

Since the default users profile data is limited, an explicit selection for the properties in the user profile are selected and returned as part of the request. The Razor component should contain an authorised view.

Note: This is sample code, so you should check for Nulls in your own implementation. In fact, you would want to ensure that you check each property and likely store the values i.e. in a container that can be then referenced by the application. Typically, you could implement the scope offline_access for the application to retrieve refresh tokens as this is explicitly required for the v2.0 token endpoint i.e. you don’t want your application to fail based on the fact it doesn’t have the necessary structure to utilise refresh tokens to request new access tokens, otherwise you will end up receiving the an error similar to the one below.

Kungfu Developer 
Profile 
First Name: 
Last Name: 
Objectld: 
Display Name: 
Company: 
Job Title: 
Department: 
Office: 
UPN: 
Office Phone: 
Log out 
About 
Code: InvalidAuthenticationToken Message: Access token has expired. Inner error: AdditionalData: date: request-id: 72849096-54fb-4199-9f17- 
d6c6a7e6bc25 ClientRequestld: 72849096-54fb-4199-9f17-d6c6a7e6bc25

Updating the LoginDisplay Razor Component

For this sample, I’ve simply updated the component to add <a href tag> around the @UserName property to access the new LCprofile Razor Component.

@inject AuthenticationStateProvider AuthNStateProvider

<AuthorizeView>
    <Authorized>
        <a href="/lcprofile" >@UserName</a>
        <a href="AzureAD/Account/SignOut">Log out</a>
    </Authorized>
    <NotAuthorized>
        <a href="AzureAD/Account/SignIn">Log in</a>
    </NotAuthorized>
</AuthorizeView>

The Result

When the user name is clicked in the header, the user navigates to the lcprofile razor component and the profile properties are displayed .

This reminds me of the days when I developed custom controls for SharePoint several years ago, for users to edit their own profiles.

This ends the sample on the update to the LoginDisplay component in Blazor server-side applications.

2 thoughts on “Blazor Apps: The Login Display – Part 2

  1. Juan

    I was following the login display but an issue that I am facing is
    @inject TokenProvider TokenProvider
    when I run it I get InvalidOperationException: Cannot provide a value for property ‘TokenProvider’ on type ‘App.Pages.LCProfile’. There is no registered service of type ‘App.Shared.TokenProvider’.
    Which TokenProvider exist under App.Shared

    I might need some help here to understand TokenProvider how to registered.

    Reply

Leave a comment