Opened

Determine if a user is in a certain group or role in a Blazor Server App?

bschmiedeler2 commented edited

My Blazor Server App authenticates the signed in user using Azure AD. We have assigned users to some groups and the groups to roles.

We have a hidden page that uses graph to get the logged in user and then spit out their information, including name and email address, and all the "claims" that they have. However, we do not see all the groups/roles a user should be in, and it just seems random to me.

I don't really care about this hidden page, we created it to try to determine why something like this

<AuthorizeView Roles="@Roles.XXXXXX">

prohibits even users who are assigned directly to the role XXXXX cannot see the view.

We have checked on the Azure side and everything looks OK. We even have a call in to MS but they don't know what is wrong.

I will include my code files, but if someone has an example of successfully returning groups or roles, I would be interested in seeing it.

The code was originally written by outside consultants, and I am trying to get it to work. I am new to .NET and C# and Blazor and Blazorise.

Any help would be greatly appreciated.

startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Blazorise;
using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome;
using FluentValidation;
using Microsoft.Identity.Web;
using WireDesk.Configuration;
using WireDesk.Models;
using WireDesk.Web.CrossCutting;
using WireDesk.Web.ViewModels;
using WireDesk.Web.Validations;
using ConfigurationForm = WireDesk.Models.ConfigurationForm;

namespace WireDesk.Web
{
    public class Startup
    {
        private readonly Bootstrap bootStrapper;
        public IConfiguration Configuration { get; }
        public SystemConfiguration SystemConfiguration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            SystemConfiguration = new SystemConfiguration();
            Configuration.GetSection("SystemConfiguration").Bind(SystemConfiguration);
            bootStrapper = new Bootstrap(SystemConfiguration);
        }


        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services
                .AddBlazorise(options =>
                {
                    options.ChangeTextOnKeyPress = true; // optional
                })
                .AddBootstrapProviders()
                .AddFontAwesomeIcons();

            //This enables your application to use the Microsoft identity platform endpoint. This endpoint is capable of signing-in users both with their Work and School and Microsoft Personal accounts.
            //services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
            //        .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "scope=Groups.Read.All" })
            //        .AddInMemoryTokenCaches(); // Adds aspnetcore MemoryCache as Token cache provider for MSAL.

            //services.AddGraphService(Configuration);    // Adds the IMSGraphService as an available service for this app.

            services.AddAuthenticationCore();
            services.AddRazorPages();
            services.AddServerSideBlazor()
                .AddCircuitOptions(option => { option.DetailedErrors = true; })
                .AddMicrosoftIdentityConsentHandler();

            bootStrapper.AddSecurity(services, Configuration);
            bootStrapper.AddServices(services);
            services.AddTransient<IValidator<OutgoingPaymentRequestForm>, CreateOutGoingRequestViewValidator>();
            services.AddTransient<IValidator<ApprovalFlowForm>, CreateUpdateApprovalFlowValidations>();
            services.AddTransient<IValidator<ConfigurationForm>, ConfigurationValidator>();
            services.AddTransient<IValidator<LocationForm>, LocationValidator>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }
    }
}

graphService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using WireDesk.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.Identity.Web;
using WireDesk.Web.ViewModels;
using Microsoft.AspNetCore.Components;
using WireDesk.Services;

namespace WireDesk.Web.ApplicationServices
{
    public interface IGraphService
    {
        Task<Microsoft.Graph.User> GetUserName();
        Task<IEnumerable<GraphUser>> GetUsers();
    }

    public class GraphService : BaseAppService<GraphService>, IGraphService
    {

        public OutgoingRequestViewModel[] _outgoingRequestList;
        public IEnumerable<OutgoingRequest> outgoingRequestList;
        private readonly GraphServiceClient graphServiceClient;
        private readonly MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler;
        private readonly IConfiguration _config;
        private readonly IWebHostEnvironment _hostEnvironment;
        public OutgoingRequestViewModel _selectedOutgoingRequest = new OutgoingRequestViewModel();
        private readonly IOutgoingRequestsReadService outgoingRequestsReadService;
        private readonly string recipient;
        private readonly SearchOptionEnums GetByToday = SearchOptionEnums.GetByDateToday;

        [Inject]
        public IOutgoingRequestsAppService OutgoingRequestsAppService { get; set; }

        public GraphService(ILogger<GraphService> logger, GraphServiceClient graphServiceClient
            , MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler
            , IConfiguration config
            , IWebHostEnvironment environment) : base(logger)
        {
            this.graphServiceClient = graphServiceClient;
            this.consentHandler = consentHandler;
            OutgoingRequestsAppService = OutgoingRequestsAppService;
            _config = config;
            _hostEnvironment = environment;
        }

        public async Task<Microsoft.Graph.User> GetUserName()

        {
            try
            {
                return await graphServiceClient.Me.Request().GetAsync();
            }
            catch (Exception ex)
            {
                consentHandler.HandleException(ex);
            }

            return null;
        }

        public async Task<IEnumerable<GraphUser>> GetUsers()
        {
            var userNames = new List<GraphUser>();
            try
            {
                var pages = new List<User>();

                var queryOptions = new List<QueryOption>()
                {
                    new QueryOption("$count", "true")
                };

                var graphUserPages = await graphServiceClient.Groups["{cb53deaa-1286-4039-9cdf-4f22dfec7a18}"].Members
                    .Request(queryOptions)
                    .Header("ConsistencyLevel", "eventual")
                    .GetAsync();
                pages.AddRange(graphUserPages.CurrentPage.OfType<User>());

                // Fetch each page and add those results to the list
                while (graphUserPages.NextPageRequest != null)
                {
                    graphUserPages = await graphUserPages.NextPageRequest.GetAsync();
                    pages.AddRange(graphUserPages.CurrentPage.OfType<User>());
                }

                foreach (var graphUser in pages)
                {
                    userNames.Add(new GraphUser { DisplayName = graphUser.DisplayName, Email = graphUser.Mail });
                }
            }
            catch (Exception ex)
            {
                consentHandler.HandleException(ex);
            }

            return userNames;
        }
    }
}

claimsIdentityProvider.cs

namespace WireDesk.Common.Identity
{
    public static class ClaimTypes
    {
        internal const string ClaimTypeIdentityNamespace = "http://schemas.microsoft.com/identity/claims";

        public const string TenantId = ClaimTypeIdentityNamespace + "/tenantid";
        public const string UserObjectId = ClaimTypeIdentityNamespace + "/objectidentifier";

        internal const string ClaimTypeNamespace = "http://schemas.microsoft.com/ws/2008/06/identity/claims";

        public const string AuthenticationInstant = ClaimTypeNamespace + "/authenticationinstant";
        public const string AuthenticationMethod = ClaimTypeNamespace + "/authenticationmethod";
        public const string CookiePath = ClaimTypeNamespace + "/cookiepath";
        public const string DenyOnlyPrimarySid = ClaimTypeNamespace + "/denyonlyprimarysid";
        public const string DenyOnlyPrimaryGroupSid = ClaimTypeNamespace + "/denyonlyprimarygroupsid";
        public const string DenyOnlyWindowsDeviceGroup = ClaimTypeNamespace + "/denyonlywindowsdevicegroup";
        public const string Dsa = ClaimTypeNamespace + "/dsa";
        public const string Expiration = ClaimTypeNamespace + "/expiration";
        public const string Expired = ClaimTypeNamespace + "/expired";
        public const string GroupSid = ClaimTypeNamespace + "/groupsid";
        public const string IsPersistent = ClaimTypeNamespace + "/ispersistent";
        public const string PrimaryGroupSid = ClaimTypeNamespace + "/primarygroupsid";
        public const string PrimarySid = ClaimTypeNamespace + "/primarysid";
        public const string Role = ClaimTypeNamespace + "/role";
        public const string SerialNumber = ClaimTypeNamespace + "/serialnumber";
        public const string UserData = ClaimTypeNamespace + "/userdata";
        public const string Version = ClaimTypeNamespace + "/version";
        public const string WindowsAccountName = ClaimTypeNamespace + "/windowsaccountname";
        public const string WindowsDeviceClaim = ClaimTypeNamespace + "/windowsdeviceclaim";
        public const string WindowsDeviceGroup = ClaimTypeNamespace + "/windowsdevicegroup";
        public const string WindowsUserClaim = ClaimTypeNamespace + "/windowsuserclaim";
        public const string WindowsFqbnVersion = ClaimTypeNamespace + "/windowsfqbnversion";
        public const string WindowsSubAuthority = ClaimTypeNamespace + "/windowssubauthority";

        //
        // From System.IdentityModel.Claims
        //
        internal const string ClaimType2005Namespace = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims";
        public const string Anonymous = ClaimType2005Namespace + "/anonymous";
        public const string Authentication = ClaimType2005Namespace + "/authentication";
        public const string AuthorizationDecision = ClaimType2005Namespace + "/authorizationdecision";
        public const string Country = ClaimType2005Namespace + "/country";
        public const string DateOfBirth = ClaimType2005Namespace + "/dateofbirth";
        public const string Dns = ClaimType2005Namespace + "/dns";
        public const string DenyOnlySid = ClaimType2005Namespace + "/denyonlysid"; // NOTE: shown as 'Deny only group SID' on the ADFSv2 UI!
        public const string Email = "preferred_username";
        public const string Gender = ClaimType2005Namespace + "/gender";
        public const string GivenName = ClaimType2005Namespace + "/givenname";
        public const string Hash = ClaimType2005Namespace + "/hash";
        public const string HomePhone = ClaimType2005Namespace + "/homephone";
        public const string Locality = ClaimType2005Namespace + "/locality";
        public const string MobilePhone = ClaimType2005Namespace + "/mobilephone";
        public const string EmailAddress = ClaimType2005Namespace + "/name";
        public const string Name = "name";
        public const string NameIdentifier = ClaimType2005Namespace + "/nameidentifier";
        public const string OtherPhone = ClaimType2005Namespace + "/otherphone";
        public const string PostalCode = ClaimType2005Namespace + "/postalcode";
        public const string Rsa = ClaimType2005Namespace + "/rsa";
        public const string Sid = ClaimType2005Namespace + "/sid";
        public const string Spn = ClaimType2005Namespace + "/spn";
        public const string StateOrProvince = ClaimType2005Namespace + "/stateorprovince";
        public const string StreetAddress = ClaimType2005Namespace + "/streetaddress";
        public const string Surname = ClaimType2005Namespace + "/surname";
        public const string System = ClaimType2005Namespace + "/system";
        public const string Thumbprint = ClaimType2005Namespace + "/thumbprint";
        public const string Upn = ClaimType2005Namespace + "/upn";
        public const string Uri = ClaimType2005Namespace + "/uri";
        public const string Webpage = ClaimType2005Namespace + "/webpage";
        public const string X500DistinguishedName = ClaimType2005Namespace + "/x500distinguishedname";
		internal const string ClaimType2009Namespace = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims";
        public const string Actor = ClaimType2009Namespace + "/actor";

    }
}
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using AutoMapper;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Graph;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using WireDesk.Common.Identity;
using WireDesk.Configuration;
using WireDesk.DataAccess;
using WireDesk.DataAccess.DataContext;
using WireDesk.DataAccess.Repositories;
using WireDesk.Models;
using WireDesk.Services;
using WireDesk.Services.Base;
using WireDesk.Services.Mapping;
using WireDesk.Services.Policies;
using WireDesk.Services.Validation;
using WireDesk.Web.ApplicationServices;
using FluentValidation;
using WireDesk.Web.ViewModels;

bootstraper.cs

namespace WireDesk.Web.CrossCutting
{
    public class Bootstrap
    {
        private readonly SystemConfiguration systemConfiguration;

        public Bootstrap(SystemConfiguration systemConfiguration)
        {
            this.systemConfiguration = systemConfiguration;
        }

        public void AddSecurity(IServiceCollection services, IConfiguration configuration)
        {

            // This is required to be instantiated before the OpenIdConnectOptions starts getting configured.
            // By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.
            // 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role' instead of 'roles'
            // This flag ensures that the ClaimsIdentity claims collection will be built from the claims in the token.
            JwtSecurityTokenHandler.DefaultMapInboundClaims = false;


            var initialScopes = configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
            // Add authentication with Microsoft identity platform.
            services.AddMicrosoftIdentityWebAppAuthentication(configuration)
                .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
                  .AddMicrosoftGraph(configuration.GetSection("DownstreamApi"))
                  .AddInMemoryTokenCaches();


            services.Configure<OpenIdConnectOptions>(
                OpenIdConnectDefaults.AuthenticationScheme, options =>
                {
                    // The claim in the Jwt token where App roles are available. 
                    options.TokenValidationParameters.ValidateIssuer = false;
                    //options.TokenValidationParameters.RoleClaimType = System.Security.Claims.ClaimTypes.Role;
                    //options.TokenValidationParameters.RoleClaimType = "roles";
                    options.TokenValidationParameters.RoleClaimType = "role";
                    options.TokenValidationParameters.NameClaimType = "name";
                });


            services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            }).AddMicrosoftIdentityUI();

            services.AddAuthorization(options =>
            {
                 // By default, all incoming requests will be authorized according to the default policy
                 options.FallbackPolicy = options.DefaultPolicy;
            });


        }

        public void AddServices(IServiceCollection services)
        {
            SetupCrossCuttingServices(services);
            SetupAppServices(services);
            SetupData(services);  
            SetupServices(services);
        }

        private void SetupData(IServiceCollection services)
        {    
            services.AddDbContextFactory<WireDeskDataContext>(opt => opt.UseSqlServer(systemConfiguration.ConnectionStrings.WireDesk));
            // Data Access
            services.AddScoped<IWeatherRepository, WeatherRepository>();
            services.AddScoped<IOutgoingRequestsRepository, OutgoingRequestsRepository>();
            services.AddScoped<ICustomerRepository, CustomerRepository>();
            services.AddScoped<IApprovalFlowRepository, ApprovalFlowRepository>();
            services.AddScoped<ILocationRepository, LocationRepository>();
            services.AddScoped<ILocationRequestPaymentRepository, LocationRequestPaymentRepository>();
            services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
            //services.AddScoped<IIntegrationContext, IntegrationContext>();
            //services.AddScoped<IDataContext, IntegrationContext>();
            //services.AddScoped<ICacheRepository, CacheRepository>();
            //services.AddScoped<ICache, DataAccess.Caching.MemoryCache>();
            //services.AddScoped<IMemoryCache, Microsoft.Extensions.Caching.Memory.MemoryCache>();
        }

        private void SetupAppServices(IServiceCollection services)
        {
            // Rest services
            // services.AddTransient<IRESTService, RESTService>();

            services.AddScoped<IWeatherForecastAppService, WeatherForecastAppService>();
            services.AddScoped<IOutgoingRequestsAppService, OutgoingRequestAppService>();
            services.AddScoped<ICustomerAppService, CustomerAppService>();
            services.AddScoped<IApprovalFlowAppService, ApprovalFlowAppService>();
            services.AddScoped<ILocationAppService, LocationAppService>();
            services.AddScoped<IConfigurationAppService, ConfigurationAppService>();
            services.AddScoped<IGraphService, GraphService>();
            services.AddHttpClient();
            services.AddScoped<HttpClient>();
        }

        private void SetupServices(IServiceCollection services)
        {
            // Rest services
            // services.AddTransient<IRESTService, RESTService>();

            services.AddScoped<IWeatherReadService, WeatherReadService>(); 
            services.AddScoped<IWeatherWriteService, WeatherWriteService>(); 
            services.AddScoped<IWeatherForecastValidator, WeatherForecastValidator>();
            services.AddScoped<IOutgoingRequestsReadService, OutgoingRequestsReadService>();
            services.AddScoped<IOutgoingRequestWriteService, OutgoingRequestsWriteService>();
            services.AddScoped<ICustomerReadService, CustomerReadService>();
            services.AddScoped<IApprovalFlowReadService, ApprovalFlowReadService>();
            services.AddScoped<IApprovalFlowWriteService, ApprovalFlowWriteService>();
            services.AddScoped<ILocationReadService, LocationReadService>();
            services.AddScoped<ILocationWriteService, LocationWriteService>();
            services.AddScoped<IConfigurationReadService, ConfigurationReadService>();
            services.AddScoped<IConfigurationWriteService, ConfigurationWriteService>();
            services.AddScoped<IAzureKeyVaultReadService, AzureKeyVaultReadService>();
            services.AddHttpClient<IOauthBearerTokenService, OAuthBearerTokenService>();
            services.AddHttpClient<ICustomerReadService, CustomerReadService>();
        }

        private void SetupCrossCuttingServices(IServiceCollection services)
        {
            // Register App Config 
 
            services.AddScoped<ISystemConfiguration>((serviceProvider) => this.systemConfiguration);

            // Logging  
            services.AddSingleton(new LoggerFactory());
            services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));

            // Policies
            services.AddScoped<IPolicyFactory<HttpRetryPolicyFactory>, HttpRetryPolicyFactory>();

            // Mapping
            //services.AddScoped<IMappingService, MappingService>();

            
            // Register Automapper
            var mapperConfig = new AutoMapper.MapperConfiguration(cfg =>
            {
                //cfg.SourceMemberNamingConvention = PascalCaseNamingConvention();
                //cfg.ConstructServicesUsing
                cfg.AllowNullDestinationValues = true;
                cfg.AddMaps(typeof(WireDesk_Mapping).Assembly);
                cfg.AddMaps(typeof(WireDesk_Entity_Mapping).Assembly);
                //cfg.AddProfile(new AutoMapperConfigMapV1Profile());
            });
            var mapper = mapperConfig.CreateMapper();
            services.AddSingleton<IMapper>(mapper);

            // Identity
            // services.AddScoped<IHttpContextFactory, DefaultHttpContextFactory>();
            // services.AddScoped<IHttpContextFactory, HttpContextFactory>();
            //services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();*/ // removed wont work on server side applications
            services.AddScoped<IPrincipalContext, ClaimsIdentityProvider>();
            services.AddScoped<IIdentityService, IdentityService>();
        }
    }
}
claimsIdentityProvider.cs
	
namespace WireDesk.Common.Identity
{
    public static class ClaimTypes
    {
        internal const string ClaimTypeIdentityNamespace = "http://schemas.microsoft.com/identity/claims";

        public const string TenantId = ClaimTypeIdentityNamespace + "/tenantid";
        public const string UserObjectId = ClaimTypeIdentityNamespace + "/objectidentifier";

        internal const string ClaimTypeNamespace = "http://schemas.microsoft.com/ws/2008/06/identity/claims";

        public const string AuthenticationInstant = ClaimTypeNamespace + "/authenticationinstant";
        public const string AuthenticationMethod = ClaimTypeNamespace + "/authenticationmethod";
        public const string CookiePath = ClaimTypeNamespace + "/cookiepath";
        public const string DenyOnlyPrimarySid = ClaimTypeNamespace + "/denyonlyprimarysid";
        public const string DenyOnlyPrimaryGroupSid = ClaimTypeNamespace + "/denyonlyprimarygroupsid";
        public const string DenyOnlyWindowsDeviceGroup = ClaimTypeNamespace + "/denyonlywindowsdevicegroup";
        public const string Dsa = ClaimTypeNamespace + "/dsa";
        public const string Expiration = ClaimTypeNamespace + "/expiration";
        public const string Expired = ClaimTypeNamespace + "/expired";
        public const string GroupSid = ClaimTypeNamespace + "/groupsid";
        public const string IsPersistent = ClaimTypeNamespace + "/ispersistent";
        public const string PrimaryGroupSid = ClaimTypeNamespace + "/primarygroupsid";
        public const string PrimarySid = ClaimTypeNamespace + "/primarysid";
        public const string Role = ClaimTypeNamespace + "/role";
        public const string SerialNumber = ClaimTypeNamespace + "/serialnumber";
        public const string UserData = ClaimTypeNamespace + "/userdata";
        public const string Version = ClaimTypeNamespace + "/version";
        public const string WindowsAccountName = ClaimTypeNamespace + "/windowsaccountname";
        public const string WindowsDeviceClaim = ClaimTypeNamespace + "/windowsdeviceclaim";
        public const string WindowsDeviceGroup = ClaimTypeNamespace + "/windowsdevicegroup";
        public const string WindowsUserClaim = ClaimTypeNamespace + "/windowsuserclaim";
        public const string WindowsFqbnVersion = ClaimTypeNamespace + "/windowsfqbnversion";
        public const string WindowsSubAuthority = ClaimTypeNamespace + "/windowssubauthority";

        //
        // From System.IdentityModel.Claims
        //
        internal const string ClaimType2005Namespace = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims";
        public const string Anonymous = ClaimType2005Namespace + "/anonymous";
        public const string Authentication = ClaimType2005Namespace + "/authentication";
        public const string AuthorizationDecision = ClaimType2005Namespace + "/authorizationdecision";
        public const string Country = ClaimType2005Namespace + "/country";
        public const string DateOfBirth = ClaimType2005Namespace + "/dateofbirth";
        public const string Dns = ClaimType2005Namespace + "/dns";
        public const string DenyOnlySid = ClaimType2005Namespace + "/denyonlysid"; // NOTE: shown as 'Deny only group SID' on the ADFSv2 UI!
        public const string Email = "preferred_username";
        public const string Gender = ClaimType2005Namespace + "/gender";
        public const string GivenName = ClaimType2005Namespace + "/givenname";
        public const string Hash = ClaimType2005Namespace + "/hash";
        public const string HomePhone = ClaimType2005Namespace + "/homephone";
        public const string Locality = ClaimType2005Namespace + "/locality";
        public const string MobilePhone = ClaimType2005Namespace + "/mobilephone";
        public const string EmailAddress = ClaimType2005Namespace + "/name";
        public const string Name = "name";
        public const string NameIdentifier = ClaimType2005Namespace + "/nameidentifier";
        public const string OtherPhone = ClaimType2005Namespace + "/otherphone";
        public const string PostalCode = ClaimType2005Namespace + "/postalcode";
        public const string Rsa = ClaimType2005Namespace + "/rsa";
        public const string Sid = ClaimType2005Namespace + "/sid";
        public const string Spn = ClaimType2005Namespace + "/spn";
        public const string StateOrProvince = ClaimType2005Namespace + "/stateorprovince";
        public const string StreetAddress = ClaimType2005Namespace + "/streetaddress";
        public const string Surname = ClaimType2005Namespace + "/surname";
        public const string System = ClaimType2005Namespace + "/system";
        public const string Thumbprint = ClaimType2005Namespace + "/thumbprint";
        public const string Upn = ClaimType2005Namespace + "/upn";
        public const string Uri = ClaimType2005Namespace + "/uri";
        public const string Webpage = ClaimType2005Namespace + "/webpage";
        public const string X500DistinguishedName = ClaimType2005Namespace + "/x500distinguishedname";
		internal const string ClaimType2009Namespace = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims";
        public const string Actor = ClaimType2009Namespace + "/actor";

    }
}

groups.razor

@page "/groups"
@using System.Security.Claims
@using WireDesk.Services.Base
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject IPrincipalContext ClaimsIdentity
@inject IGraphService graphService

<h3>Groups</h3>
 
<AuthorizeView Roles="Role.Wiredesk">
    <Authorized>
        <p>Auth: Wiredesk</p>
        <p>Principal Context Name: @Name</p>
        <p>Principal Context Email: @Email</p>
    </Authorized>
</AuthorizeView>

<AuthorizeView Roles=@Roles.Admin>
    <Authorized>
        <p>you don't see Admin- because it has a 0 guid</p>
    </Authorized>
</AuthorizeView>

@if (claims.Count() > 0)
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}

@code {
    [CascadingParameter]
    private Task<AuthenticationState> authenticationStateTask { get; set; }
    private ClaimsPrincipal user { get; set; }
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();
    public string Name { get; set; }
    public string Email { get; set; }
    public string Role { get; set; }
    public bool isVisible;

    protected override async Task OnInitializedAsync()
    {
        /*This is all you need to really do. Call the claims identity service and fetch what you need */
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        user = authState.User;
        claims = user.Claims; 
        Name = ClaimsIdentity.CurrentIdentity.Name;
        Email = ClaimsIdentity.EmailAddress;
        Role = ClaimsIdentity.RoleName;
        StateHasChanged();
    }
}
mladenmacanovic commented

Wow, that's a lot of code.

I can only suspect the problem is that Role and Roles are null when you open the page. So if fails with an exception. Do you have anything in the console?

Try with the following

<AuthorizeView Roles="Role?.Wiredesk">
    <Authorized>
        <p>Auth: Wiredesk</p>
        <p>Principal Context Name: @Name</p>
        <p>Principal Context Email: @Email</p>
    </Authorized>
</AuthorizeView>

<AuthorizeView Roles=@Roles?.Admin>
    <Authorized>
        <p>you don't see Admin- because it has a 0 guid</p>
    </Authorized>
</AuthorizeView>

Also, can you tell me what Blazorise version are you using? Based on the ChangeTextOnKeyPress I would say it is something older than v1.x.

bschmiedeler2 commented

I often see people who do not put in enough code; I realize this was a ton of code, just not sure what parts to cut out.

Thank you for your response.

We are using version 0.9.5.6. I believe hour .NET version is not compatible with the newer versions of Blazorise.

I think the most relevant part of the code is

   protected override async Task OnInitializedAsync()
    {
        /*This is all you need to really do. Call the claims identity service and fetch what you need */
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        user = authState.User;
        claims = user.Claims; 
        Name = ClaimsIdentity.CurrentIdentity.Name;
        Email = ClaimsIdentity.EmailAddress;
        Role = ClaimsIdentity.RoleName;
        StateHasChanged();
    }

which is the last thing which I listed in the code above. The reason is that when groups is executed this code gets the user in user = authStateUser and then gets the claims. I can inspect the claims by debugging here, and when the groups code executes, it displays a whole bunch of information about the user.

I tried to paste in a screen shot of part of the list of roles, but I could not, but the output looks basically like this:

roles: PowerPlatformLeads
roles: ITGroup
roles: AnotherRole
roles: AnotherRole

So I can see all of the claims that are being spit out by this code. I only see 46 roles, and nothing to do with may application.

Understand that this question doesn't deal with Blazorise directly, but was hoping you might be able to offer additional direction to me. MS support told me to download and install Fiddler to see if the JWToken getting sent down to the app actually has the correct information (i.e. the additional roles), so we can determine if this problem is due to something incorrect in our Azure configuration or on the applicaton side. That should tell me where to look for the problem. I plan on doing that tomorrow.

Do you know of any straightforward example code of how to configure roles for an Azure app so that Blazor can grab roles? I might be able to use that to solve my problem.

Once again, thank you for your assistance.

Bryan

mladenmacanovic commented

Unfortunately I personally cannot help you much because I never worked with Azure. We have our own servers and all our applications are running on IIS. You could try inspecting code of this very same forum for an inspiration. As a commercial user you have the rights to access it on GitHub private repos.

Just send me your GH username in.

bschmiedeler2 commented

My GH username is bryanschmiedeler.

Thank you very much!

Bryan

Want to comment on this issue? Log in and write your comment.
Asignee
No assignee
Labels
No Labels