0

I have an application in .netcore 3.1 I need to carry out an embedding of power bi boards for my organization, under the technique: userOwnsData. The problem I am having is that my app already has logging through the active directory, and when trying to add the logic of the power bi embed it somehow collides or conflicts with what was working. The color data ?: Tenant + appId + Secret are the same key. Maybe there is a way to take my token generated by active directory and put it to power bi, I don't know.

I started downloading the code provided by microsoft in this guide, it executes correctly when entering TenantId, ApplicationId, and secret. This app works perfectly, the problem is when I try to integrate this code in my existing application.

Embed Power BI content using a sample embed for your organization

My application also uses active directory for user login, the embed and show power bi reports section is a module of it. So, I think I have a problem by trying to use two different configurations to log in using azure tools... I am sharing my code of app settings below, the startup and the controller to embed dashboards.

        {
    "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "Mail": {
    "ApiKeySendGrid": "xxxsecretxxx"
  },
  "AllowedHosts": "*",

  //Active Directory
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com",
    "Domain": "organization domain",
    "TenantId": "xxxsecretxxx",
    "ClientId": "xxxsecretxxx",
    "CallbackPath": "/signin-oidc",
    "SignedOutCallbackPath": "/signout-oidc"
  },

  //PBI EMBED USER OWNS DATA
  "AzureAd2": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "https://api.powerbi.com",
    "TenantId": "xxxsecretxxx",
    "ClientId": "xxxsecretxxx",
    "ClientSecret": "xxxsecretxxx",
    "CallbackPath": "/signin-oidc2",
  },
  "PowerBiHostname": "https://app.powerbi.com"
}

My startup:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NFP.Repository.Contexts;
using DB_Admin.Filters;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NFP.IRepository;
using NFP.Repository.MySQL;
using NFP.IAplication;
using NFP.Aplication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Http.Features;

namespace NFP.DataClinic.Presentation
{
    public static class InjectionStart
    {
        public static IServiceCollection ResolveThisContext(this IServiceCollection services, IConfiguration Configuration) => services
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
                       // Graph api base url
        var graphBaseUrl = "https://graph.microsoft.com/v1.0";

        // Graph scope for reading logged in user's info
        var userReadScope = "user.read";

        // List of scopes required
        string[] initialScopes = new string[] { userReadScope };

        

        services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd");

        services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd2")
            .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
            .AddMicrosoftGraph(graphBaseUrl, userReadScope)
            .AddSessionTokenCaches();

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

            //Dependency Injection
            services.ResolveThisContext(Configuration);
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.Configure<FormOptions>(options => options.ValueCountLimit = 5000);
            services.AddMvc(config =>
            {
                config.Filters.Add(new CustomActionFilter());
            });

            services.AddMvc()
                    .AddMvcOptions(options =>
                    {
                        options.MaxModelBindingCollectionSize = int.MaxValue;
                        options.MaxModelValidationErrors = 999999;
                    });

            services.AddDistributedMemoryCache();
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromDays(1); // It depends on user requirements.
            });

            var config = new ConfigurationBuilder() //newForAD
                .SetBasePath(System.IO.Directory.GetCurrentDirectory())//newForAD
                .AddJsonFile("appsettings.json", false)//newForAD
                .Build();//newForAD

        }
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
        public class RequestFormSizeLimitAttribute : Attribute, IAuthorizationFilter, IOrderedFilter
        {
            private readonly FormOptions _formOptions;

            public RequestFormSizeLimitAttribute(int valueCountLimit)
            {
                _formOptions = new FormOptions()
                {
                    ValueCountLimit = valueCountLimit
                };
            }

            public int Order { get; set; }

            public void OnAuthorization(AuthorizationFilterContext context)
            {
                var features = context.HttpContext.Features;
                var formFeature = features.Get<IFormFeature>();

                if (formFeature == null || formFeature.Form == null)
                {
                    // Request form has not been read yet, so set the limits
                    features.Set<IFormFeature>(new FormFeature(context.HttpContext.Request, _formOptions));
                }
            }
        }
        // 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("/Home/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.UseCookiePolicy();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseSession(); //cambio
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                //endpoints.MapRazorPages();
            });
        }
    }
    
}

In this statement the problem seems to be, if I leave the original version:

services.AddMicrosoftIdentityWebAppAuthentication (Configuration)

my app runs as it had been working, with login through active directory correctly, the guide suggests adding this so that it works also logging to power bi (the last 3 lines) but that generates a session error and even the active directory stops working.

So, currently trying this one, it says to me that : System.InvalidOperationException: 'Scheme already exists: Cookies'

        services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd");

        services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd2")
            .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
            .AddMicrosoftGraph(graphBaseUrl, userReadScope)
            .AddSessionTokenCaches();

Note that in my lack of experience I tried to call AzureAd2 to my new app settings configuration, thinking that this way I could take them well, but it doesn't work either.

Update:

Now that I know that is neccesary to get the AD token and use it to authenticate trough power bi, I am listing here the controller in charge of executing the token acquisition method...

    / ----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// ----------------------------------------------------------------------------

namespace DataClinic.Presentation.Controllers
{
    using Domain.PbiEmbedUOD;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Identity.Web;
    using Microsoft.Graph;
    using System.Threading.Tasks;

    [Authorize]
    public class ReportsController : Controller
    {
        private readonly GraphServiceClient m_graphServiceClient;

        private readonly ITokenAcquisition m_tokenAcquisition;

        public ReportsController(ITokenAcquisition tokenAcquisition,
                              GraphServiceClient graphServiceClient)
        {
            this.m_tokenAcquisition = tokenAcquisition;
            this.m_graphServiceClient = graphServiceClient;
        }

        public IActionResult Index()
        {
            return View();
        }

        // Redirects to login page to request increment consent
        [AuthorizeForScopes(Scopes = new string[] { PowerBiScopes.ReadDashboard, PowerBiScopes.ReadReport, PowerBiScopes.ReadWorkspace } )]
        public async Task<IActionResult> Embed()
        {
            // Generate token for the signed in user
            var accessToken = await m_tokenAcquisition.GetAccessTokenForUserAsync(new string[] { PowerBiScopes.ReadDashboard, PowerBiScopes.ReadReport, PowerBiScopes.ReadWorkspace });

            // Get username of logged in user

            var userInfo = await m_graphServiceClient.Me.Request().GetAsync();
            var userName = userInfo.DisplayName;

            AuthDetails authDetails = new AuthDetails
            {
                UserName = userName,
                AccessToken = accessToken
            };

            return View(authDetails);
        }
    }
}

0 Answers0