Forum Discussion
Operation returned an invalid status code 'Unauthorized' at GetReportInGroupWithHttpMessagesAsync
Hi v-chenwuz-msft , Anonymous
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 App
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
- v-yanjiang-msftCommunity 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.
Best Regards,
Community Support Team _ kalyjIf this post helps, then please considerAccept it as the solution to help the other members find it more quickly.
- AnonymousNot applicable
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?
- v-yanjiang-msftCommunity Support
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 _ kalyjIf this post helps, then please considerAccept it as the solution to help the other members find it more quickly.