I have an ASP.Net core 1.1 website and I want to embed power BI reports into the site.
Azure Hosted Data documentation: https://powerbi.microsoft.com/en-us/documentation/powerbi-developer-embed-sample-app-owns-data/
Using the App Owns Data sample found at https://github.com/Microsoft/PowerBI-Developer-Samples I have a working embedded reports solution using the sample. However, the sample project is running on .Net Framework 4.5.2 and When trying to migrate the solution into my .Net core application I have migrated the code however the ActiveDirectorylibrary (Microsoft.IdentityModel.Clients.ActiveDirectorylibrary) in .Net Core does not contain a method for UserPasswordCredential
var credential = new UserPasswordCredential(Username, Password);
The solution that I found recommended online for ASP.Net core is to use a tag helper however now that the Power BI Embedded and Power BI service have converged with the new arrival of Power BI Premium I dont think think this solutions is possible due to the depencency on token authentication for the App Hosted data.
Full Report Controller Method:
public class ReportController : Controller
{
private static readonly string Username = ConfigurationManager.AppSettings["pbiUsername"];
private static readonly string Password = ConfigurationManager.AppSettings["pbiPassword"];
private static readonly string AuthorityUrl = ConfigurationManager.AppSettings["authorityUrl"];
private static readonly string ResourceUrl = ConfigurationManager.AppSettings["resourceUrl"];
private static readonly string ClientId = ConfigurationManager.AppSettings["clientId"];
private static readonly string ApiUrl = ConfigurationManager.AppSettings["apiUrl"];
private static readonly string GroupId = ConfigurationManager.AppSettings["groupId"];
public async Task<ActionResult> EmbedReport()
{
// Create a user password cradentials.
var credential = new UserPasswordCredential(Username, Password);
// Authenticate using created credentials
var authenticationContext = new AuthenticationContext(AuthorityUrl);
var authenticationResult = await authenticationContext.AcquireTokenAsync(ResourceUrl, ClientId, credential);
if (authenticationResult == null)
{
return View(new EmbedConfig()
{
ErrorMessage = "Authentication Failed."
});
}
var tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
// Create a Power BI Client object. It will be used to call Power BI APIs.
using (var client = new PowerBIClient(new Uri(ApiUrl), tokenCredentials))
{
// Get a list of reports.
var reports = await client.Reports.GetReportsInGroupAsync(GroupId);
// Get the first report in the group.
var report = reports.Value.FirstOrDefault();
if (report == null)
{
return View(new EmbedConfig()
{
ErrorMessage = "Group has no reports."
});
}
// Generate Embed Token.
var generateTokenRequestParameters = new GenerateTokenRequest(accessLevel: "view");
var tokenResponse = await client.Reports.GenerateTokenInGroupAsync(GroupId, report.Id, generateTokenRequestParameters);
if (tokenResponse == null)
{
return View(new EmbedConfig()
{
ErrorMessage = "Failed to generate embed token."
});
}
// Generate Embed Configuration.
var embedConfig = new EmbedConfig()
{
EmbedToken = tokenResponse,
EmbedUrl = report.EmbedUrl,
Id = report.Id
};
return View(embedConfig);
}
}
}
What I have tried:
Adding a reference to .Net Framework 4.6.1 in the .Net Core project to expose the .Net Framework and allow the use of the .Net equivalent of the IdentityModel.Clients.ActiveDirectory but that did not seem to help.
How can I fix the library issue and embedding in .Net core?
EDIT - Answer
Based off of the answer provided by @Fei Xue I wrote an HTTP helper class that performed a post to the API. Based off of the returned JSON I create an object and used the now available authentication token
Helper Class:
#region Settings
public static string BaseUrl
{
get
{
return "https://login.microsoftonline.com/common/oauth2/token";
}
}
#endregion
public static async Task<HttpResponseMessage> MakeAsyncRequest(string url, Dictionary<string, string> content)
{
var httpClient = new HttpClient
{
Timeout = new TimeSpan(0, 5, 0),
BaseAddress = new Uri(url)
};
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type: application/x-www-form-urlencoded", "application/json");
if (content == null)
{
content = new Dictionary<string, string>();
}
var encodedContent = new FormUrlEncodedContent(content);
var result = await httpClient.PostAsync(httpClient.BaseAddress, encodedContent);
return result;
}
Object:
public class AAD
{
public string token_type { get; set; }
public string scope { get; set; }
public string expires_in { get; set; }
public string ext_expires_in { get; set; }
public string expires_on { get; set; }
public string not_before { get; set; }
public string resource { get; set; }
public string access_token { get; set; }
public string refresh_token { get; set; }
}
Call from the controller:
var url = APIHelper.BaseUrl;
var content = new Dictionary<string, string>();
content["grant_type"] = "password";
content["resource"] = "https://analysis.windows.net/powerbi/api";
content["username"] = "<username>";
content["password"] = "<password>";
content["client_id"] = "<clientid>";
var response = await APIHelper.MakeAsyncRequest(url, content);
var result = response.Content.ReadAsStringAsync().Result;
var AAD = JsonConvert.DeserializeObject<AAD>(result);
See Question&Answers more detail:
os