Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
Anonymous
Not applicable

Operation returned an invalid status code 'Unauthorized' at GetReportInGroupWithHttpMessagesAsync

Hi @v-chenwuz-msft , @v-yiruan-msft 

I am getting errors when embedding a power bi report into a web application.

My organization's IT Team configure an Azure App and Service Principal with grant the Power BI Service for authentication and provided me ApplicationName, ClientId, ClientSecret, and TenantId. I integrated embed with customer functionality with sample code (Power BI .Net Core). In appsettings.json, I have changed clientID, clientSecret, tenantID, workspaceID, reportID and authentication mode as 'ServicePrincipal'. Still got an Unauthorized error.

 

Error statement -

Operation returned an invalid status code 'Unauthorized'
at Microsoft.PowerBI.Api.ReportsOperations.GetReportInGroupWithHttpMessagesAsync(Guid groupId, Guid reportId, Dictionary`2 customHeaders, CancellationToken cancellationToken)
at Microsoft.PowerBI.Api.ReportsOperationsExtensions.GetReportInGroupAsync(IReportsOperations operations, Guid groupId, Guid reportId, CancellationToken cancellationToken)
at Microsoft.PowerBI.Api.ReportsOperationsExtensions.GetReportInGroup(IReportsOperations operations, Guid groupId, Guid reportId)
at AppOwnsData.Services.PbiEmbedService.GetEmbedParams(Guid workspaceId, Guid reportId, Guid additionalDatasetId) in D:\Download\New\PowerBI-Developer-Samples\PowerBI-Developer-Samples-master\.NET Core\Embed for your customers\AppOwnsData\Services\PbiEmbedService.cs:line 49
at AppOwnsData.Controllers.EmbedInfoController.GetEmbedInfo() in D:\Download\New\PowerBI-Developer-Samples\PowerBI-Developer-Samples-master\.NET Core\Embed for your customers\AppOwnsData\Controllers\EmbedInfoController.cs:line 45

 

Simple, ASP.Net Core AppGrantAPIPermission.pngAdminPortal.png

 

index.js

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

$(function () {
var models = window["powerbi-client"].models;
var reportContainer = $("#report-container").get(0);

$.ajax({
type: "GET",
url: "/embedinfo/getembedinfo",
success: function (data) {
embedParams = $.parseJSON(data);
reportLoadConfig = {
type: "report",
tokenType: models.TokenType.Aad,
accessToken: embedParams.EmbedToken.Token,
// You can embed different reports as per your need
embedUrl: embedParams.EmbedReport[0].EmbedUrl,

// Enable this setting to remove gray shoulders from embedded report
// settings: {
// background: models.BackgroundType.Transparent
// }
};

// Use the token expiry to regenerate Embed token for seamless end user experience
// Refer https://aka.ms/RefreshEmbedToken
tokenExpiry = embedParams.EmbedToken.Expiration;

// Embed Power BI report when Access token and Embed URL are available
var report = powerbi.embed(reportContainer, reportLoadConfig);

// Clear any other loaded handler events
report.off("loaded");

// Triggers when a report schema is successfully loaded
report.on("loaded", function () {
console.log("Report load successful");
});

// Clear any other rendered handler events
report.off("rendered");

// Triggers when a report is successfully embedded in UI
report.on("rendered", function () {
console.log("Report render successful");
});

// Clear any other error handler events
report.off("error");

// Handle embed errors
report.on("error", function (event) {
var errorMsg = event.detail;

// Use errorMsg variable to log error in any destination of choice
console.error(errorMsg);
return;
});
},
error: function (err) {

// Show error container
var errorContainer = $(".error-container");
$(".embed-container").hide();
errorContainer.show();

// Format error message
var errMessageHtml = "<strong> Error Details: </strong> <br/>" + err.responseText;
errMessageHtml = errMessageHtml.split("\n").join("<br/>");

// Show error message on UI
errorContainer.append(errMessageHtml);
}
});
});

 

EmbeddedInfoController.cs

public class EmbedInfoController : Controller
{
private readonly PbiEmbedService pbiEmbedService;
private readonly IOptions<AzureAd> azureAd;
private readonly IOptions<PowerBI> powerBI;

public EmbedInfoController(PbiEmbedService pbiEmbedService, IOptions<AzureAd> azureAd, IOptions<PowerBI> powerBI)
{
this.pbiEmbedService = pbiEmbedService;
this.azureAd = azureAd;
this.powerBI = powerBI;
}

/// <summary>
/// Returns Embed token, Embed URL, and Embed token expiry to the client
/// </summary>
/// <returns>JSON containing parameters for embedding</returns>
[HttpGet]
public string GetEmbedInfo()
{
try
{
// Validate whether all the required configurations are provided in appsettings.json
string configValidationResult = ConfigValidatorService.ValidateConfig(azureAd, powerBI);
if (configValidationResult != null)
{
HttpContext.Response.StatusCode = 400;
return configValidationResult;
}

EmbedParams embedParams = pbiEmbedService.GetEmbedParams(new Guid(powerBI.Value.WorkspaceId), new Guid(powerBI.Value.ReportId));
return JsonSerializer.Serialize<EmbedParams>(embedParams);
}
catch (Exception ex)
{
HttpContext.Response.StatusCode = 500;
return ex.Message + "\n\n" + ex.StackTrace;
}
}
}

 

PbiEmbedService.cs

public class PbiEmbedService
{
private readonly AadService aadService;
private readonly string urlPowerBiServiceApiRoot = "https://api.powerbi.com";

public PbiEmbedService(AadService aadService)
{
this.aadService = aadService;
}

/// <summary>
/// Get Power BI client
/// </summary>
/// <returns>Power BI client object</returns>
public PowerBIClient GetPowerBIClient()
{
var tokenCredentials = new TokenCredentials(aadService.GetAccessToken(), "Bearer");
return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials);
}

/// <summary>
/// Get embed params for a report
/// </summary>
/// <returns>Wrapper object containing Embed token, Embed URL, Report Id, and Report name for single report</returns>
public EmbedParams GetEmbedParams(Guid workspaceId, Guid reportId, [Optional] Guid additionalDatasetId)
{
PowerBIClient pbiClient = this.GetPowerBIClient();

var client = JsonSerializer.Serialize(pbiClient);

// Get report info
var pbiReport = pbiClient.Reports.GetReportInGroup(workspaceId, reportId);

// Check if dataset is present for the corresponding report
// If isRDLReport is true then it is a RDL Report
var isRDLReport = String.IsNullOrEmpty(pbiReport.DatasetId);

EmbedToken embedToken;

// Generate embed token for RDL report if dataset is not present
if (isRDLReport)
{
// Get Embed token for RDL Report
embedToken = GetEmbedTokenForRDLReport(workspaceId, reportId);
}
else
{
// Create list of datasets
var datasetIds = new List<Guid>();

// Add dataset associated to the report
datasetIds.Add(Guid.Parse(pbiReport.DatasetId));

// Append additional dataset to the list to achieve dynamic binding later
if (additionalDatasetId != Guid.Empty)
{
datasetIds.Add(additionalDatasetId);
}

// Get Embed token multiple resources
embedToken = GetEmbedToken(reportId, datasetIds, workspaceId);
}

// Add report data for embedding
var embedReports = new List<EmbedReport>() {
new EmbedReport
{
ReportId = pbiReport.Id, ReportName = pbiReport.Name, EmbedUrl = pbiReport.EmbedUrl
}
};

// Capture embed params
var embedParams = new EmbedParams
{
EmbedReport = embedReports,
Type = "Report",
EmbedToken = embedToken
};

return embedParams;
}

/// <summary>
/// Get embed params for multiple reports for a single workspace
/// </summary>
/// <returns>Wrapper object containing Embed token, Embed URL, Report Id, and Report name for multiple reports</returns>
/// <remarks>This function is not supported for RDL Report</remakrs>
public EmbedParams GetEmbedParams(Guid workspaceId, IList<Guid> reportIds, [Optional] IList<Guid> additionalDatasetIds)
{
// Note: This method is an example and is not consumed in this sample app

PowerBIClient pbiClient = this.GetPowerBIClient();

// Create mapping for reports and Embed URLs
var embedReports = new List<EmbedReport>();

// Create list of datasets
var datasetIds = new List<Guid>();

// Get datasets and Embed URLs for all the reports
foreach (var reportId in reportIds)
{
// Get report info
var pbiReport = pbiClient.Reports.GetReportInGroup(workspaceId, reportId);

datasetIds.Add(Guid.Parse(pbiReport.DatasetId));

// Add report data for embedding
embedReports.Add(new EmbedReport { ReportId = pbiReport.Id, ReportName = pbiReport.Name, EmbedUrl = pbiReport.EmbedUrl });
}

// Append to existing list of datasets to achieve dynamic binding later
if (additionalDatasetIds != null)
{
datasetIds.AddRange(additionalDatasetIds);
}

// Get Embed token multiple resources
var embedToken = GetEmbedToken(reportIds, datasetIds, workspaceId);

// Capture embed params
var embedParams = new EmbedParams
{
EmbedReport = embedReports,
Type = "Report",
EmbedToken = embedToken
};

return embedParams;
}

/// <summary>
/// Get Embed token for single report, multiple datasets, and an optional target workspace
/// </summary>
/// <returns>Embed token</returns>
/// <remarks>This function is not supported for RDL Report</remakrs>
public EmbedToken GetEmbedToken(Guid reportId, IList<Guid> datasetIds, [Optional] Guid targetWorkspaceId)
{
PowerBIClient pbiClient = this.GetPowerBIClient();

// Create a request for getting Embed token
// This method works only with new Power BI V2 workspace experience
var tokenRequest = new GenerateTokenRequestV2(

reports: new List<GenerateTokenRequestV2Report>() { new GenerateTokenRequestV2Report(reportId) },

datasets: datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList(),

targetWorkspaces: targetWorkspaceId != Guid.Empty ? new List<GenerateTokenRequestV2TargetWorkspace>() { new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId) } : null
);

// Generate Embed token
var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest);

return embedToken;
}

/// <summary>
/// Get Embed token for multiple reports, datasets, and an optional target workspace
/// </summary>
/// <returns>Embed token</returns>
/// <remarks>This function is not supported for RDL Report</remakrs>
public EmbedToken GetEmbedToken(IList<Guid> reportIds, IList<Guid> datasetIds, [Optional] Guid targetWorkspaceId)
{
// Note: This method is an example and is not consumed in this sample app

PowerBIClient pbiClient = this.GetPowerBIClient();

// Convert report Ids to required types
var reports = reportIds.Select(reportId => new GenerateTokenRequestV2Report(reportId)).ToList();

// Convert dataset Ids to required types
var datasets = datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList();

// Create a request for getting Embed token
// This method works only with new Power BI V2 workspace experience
var tokenRequest = new GenerateTokenRequestV2(

datasets: datasets,

reports: reports,

targetWorkspaces: targetWorkspaceId != Guid.Empty ? new List<GenerateTokenRequestV2TargetWorkspace>() { new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId) } : null
);

// Generate Embed token
var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest);

return embedToken;
}

/// <summary>
/// Get Embed token for multiple reports, datasets, and optional target workspaces
/// </summary>
/// <returns>Embed token</returns>
/// <remarks>This function is not supported for RDL Report</remakrs>
public EmbedToken GetEmbedToken(IList<Guid> reportIds, IList<Guid> datasetIds, [Optional] IList<Guid> targetWorkspaceIds)
{
// Note: This method is an example and is not consumed in this sample app

PowerBIClient pbiClient = this.GetPowerBIClient();

// Convert report Ids to required types
var reports = reportIds.Select(reportId => new GenerateTokenRequestV2Report(reportId)).ToList();

// Convert dataset Ids to required types
var datasets = datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList();

// Convert target workspace Ids to required types
IList<GenerateTokenRequestV2TargetWorkspace> targetWorkspaces = null;
if (targetWorkspaceIds != null)
{
targetWorkspaces = targetWorkspaceIds.Select(targetWorkspaceId => new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId)).ToList();
}

// Create a request for getting Embed token
// This method works only with new Power BI V2 workspace experience
var tokenRequest = new GenerateTokenRequestV2(

datasets: datasets,

reports: reports,

targetWorkspaces: targetWorkspaceIds != null ? targetWorkspaces : null
);

// Generate Embed token
var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest);

return embedToken;
}

/// <summary>
/// Get Embed token for RDL Report
/// </summary>
/// <returns>Embed token</returns>
public EmbedToken GetEmbedTokenForRDLReport(Guid targetWorkspaceId, Guid reportId, string accessLevel = "view")
{
PowerBIClient pbiClient = this.GetPowerBIClient();

// Generate token request for RDL Report
var generateTokenRequestParameters = new GenerateTokenRequest(
accessLevel: accessLevel
);

// Generate Embed token
var embedToken = pbiClient.Reports.GenerateTokenInGroup(targetWorkspaceId, reportId, generateTokenRequestParameters);

return embedToken;
}
}

3 REPLIES 3
v-yanjiang-msft
Community Support
Community Support

Hi @Anonymous ,

According to this document, there is a note:


When using a service principal, it's recommended to limit its access to the tenant settings using a security group. To learn more about this feature, see these sections in the service principal article:


So, try to change the Apply option to The entire organization.

vkalyjmsft_0-1647832880585.png

Best Regards,
Community Support Team _ kalyj

If this post helps, then please considerAccept it as the solution to help the other members find it more quickly.

 

Anonymous
Not applicable

Hi @v-yanjiang-msft 

 

Thank you for your reply.

 

But it's recommended to limit its access to the tenant settings using a security group (which the IT team has created with the new security group and I am already added to the same group).

 

Is it necessary to change to 'The Entire Organization' as we want for the limited employee in the organization, not the entire organization?

Hi @Anonymous,

Sorry I misunderstood it, I found some solved thread about this problem for your reference:

Power Bi REST API - 401 Authorization error when u... - Microsoft Power BI Community

Unauthorized response on GetReportInGroupAsync PowerBI Embedded API call using Service Principal

Unauthorized (401) listing synchronization jobs for service principal in MS Graph API - Microsoft

Hope it helps!

 

Best Regards,
Community Support Team _ kalyj

If this post helps, then please considerAccept it as the solution to help the other members find it more quickly.

 

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors
Top Kudoed Authors