401 - Unauthorized error during Dataset Refresh PowerBI - powerbi

I am trying to do the Dataset refresh for a PowerBI Report. I created the gateway and I am able to do the dataset refresh from the admin portal. I could validate that the refresh happend successfully from UI i.e. Last Refresh column in the Admin portal. But when I try to do the refresh from a C# webapi code, I am getting the below mentioned error.
Error Message:
The remote server returned an error: (401) Unauthorized.
Stack Trace:
at System.Net.HttpWebRequest.GetResponse()
at BlueSkyPowerBIService.Controllers.PowerBIController.<RefreshDatasetsForReports>d__13.MoveNext() in C:\Krishnan\RSI\SourceCode\Bluesky Developement\BlueSky Development\BlueSkyPowerBIService\BlueSkyPowerBIService\Controllers\PowerBIController.cs:line 258
Before the refresh code, I am able to do the authentication agains the Azure AD and it succeeds and generated the auth token, but when it call the API to refresh it crashes with the above said error.
Please find my code which i am use for data refresh
List<ReportDetails> reportDetailsList = new List<ReportDetails>();
var result = new EmbedConfig();
ReportDetails reportDetails = new ReportDetails();
try
{
result = new EmbedConfig { Username = username, Roles = roles };
var error = GetWebConfigErrors();
if (error != null)
{
result.ErrorMessage = error;
//return View(result);
return null;
}
var credential = new UserPasswordCredential(Username, Password);
var authenticationContext = new AuthenticationContext(AuthorityUrl);
var authenticationResult = await authenticationContext.AcquireTokenAsync(ResourceUrl, ClientId, credential);
if (authenticationResult == null)
{
result.ErrorMessage = "Authentication Failed.";
//return View(result);
return null;
}
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.GetReportsAsync();
for (int index = 0; index < reports.Value.Count; index++)
{
reportDetails = new ReportDetails();
Report report = reports.Value[index];
HttpWebRequest request;
if (report.Id == "6317f207-57d3-4f5f-9644-18bfbb9bef99")
{
var url = "https://api.powerbi.com/v1.0/myorg/groups/{0}/datasets/{1}/refreshes";
request = System.Net.HttpWebRequest.CreateHttp(String.Format(url, GroupId, report.DatasetId));
request.KeepAlive = true;
request.Method = "POST";
request.ContentLength = 0;
request.Headers.Add("Authorization", String.Format("Bearer {0}", authenticationResult.AccessToken));
using (Stream writer = request.GetRequestStream())
{
var response = (HttpWebResponse)request.GetResponse();
Console.WriteLine("Dataset refresh request{0}", response.StatusCode.ToString());
}
}
}//end for(int index=0; index< reports.Value.Count; index++)
return reportDetailsList;
}
}
catch (HttpOperationException exc)
{
result.ErrorMessage = string.Format("Status: {0} ({1})\r\nResponse: {2}\r\nRequestId: {3}", exc.Response.StatusCode, (int)exc.Response.StatusCode, exc.Response.Content, exc.Response.Headers["RequestId"].FirstOrDefault());
}
catch (Exception exc)
{
result.ErrorMessage = exc.ToString();
}
I have granted all the required permissions in Azure portal under App Registrations,
Any ideas why I am getting this error? How to fix this issue?

This looks like a wrong configuration on the permissions of the Azure Active Directory application you are using to perform the refresh. As shown here we need to register a native app and declare some permissions to be able to access the Power BI rest API.
You need to make sure that this app has the Dataset.ReadWrite.All permission. You can see and even change permissions of the application in Azure portal under Azure Active Directory -> App Registrations. If you can't see the application select All apps from the drop down on the right.
This is how our application looks like that we are using successfully to perform a refresh through the rest API

Related

Amazon Cognito Identity with API Gateway

I'm develop app IOS and Android with Xamarin cross-platform.
I'm trying hard to use credentials receiving from Cognito Identity to authorize app invoking API Gateway.
My user flow is:
Authenticate on Cognito User Pool and get tokens
Exchange tokens for credentials on Cognito Identity Pools
Access API Gateway using credentials retrieved in the previous step.
Step 1 and 2 seems to works fine. But when app try to connect to api gateway it's getting error 403 (Forbidden).
My code:
Authenticate on Cognito User Pool and get tokens
**
public async Task<AppUser> Login(string username, string password)
{
CognitoUser cognitoUser = new CognitoUser(username, Aws.COGNITO_CLIENT_ID, CognitoUserPool, CognitoIdentityProviderClient);
AppUser appUser = new AppUser() { Email = username };
// Send a login request and wait for the response from Amazon
try
{
AuthFlowResponse response = await cognitoUser.StartWithSrpAuthAsync(new InitiateSrpAuthRequest()
{
Password = password
});
;
appUser.IsAuthenticated = true;
}
catch (NotAuthorizedException e)
{
appUser.IsAuthenticated = false;
appUser.ErrorMessage = e.Message;
}
await _tokenManagement.SaveTokens(cognitoUser.SessionTokens) ;
return appUser;
}
Exchange tokens for credentials on Cognito Identity Pools
**
public async Task<ImmutableCredentials> GetAppCredentialsAsync()
{
CognitoAWSCredentials cac;
if (_tokenManagement.CheckIsAnonymous())
{
//Anonymous credentials
cac = new CognitoAWSCredentials(Aws.COGINITO_IDENTITY_POLL_ID, RegionEndpoint.USEast1);
}
else
{
//Retrieve saved tokens from previous authentication
var tm = await _tokenManagement.RetrieveTokens();
CognitoUser user = new CognitoUser(null, Aws.COGNITO_CLIENT_ID, CognitoUserPool, CognitoIdentityProviderClient)
{
SessionTokens = new CognitoUserSession(tm.IdToken, tm.AccessToken, tm.RefreshToken, tm.IssuedTime, tm.ExpirationTime)
};
//Retrieve authenticated credentials
cac = user.GetCognitoAWSCredentials(Aws.COGINITO_IDENTITY_POLL_ID, RegionEndpoint.USEast1);
}
}
return await cac.GetCredentialsAsync();
}
Access API Gateway using credentials retrieved in the previous step:
**
public async Task<IList<MediaImage>> GetCoversAWSAsync()
{
// Getting credentials and sign request
var request = await BuildRequestAsync("/listMedia");
var client = new HttpClient();
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
IList<MediaImage> covers = JsonConvert.DeserializeObject<IList<MediaImage>>(await response.Content.ReadAsStringAsync());
return covers;
}
private async Task<HttpRequestMessage> BuildRequestAsync(string service)
{
var request = new HttpRequestMessage()
{
Method = HttpMethod.Get,
RequestUri = new Uri(baseURL + service)
};
ImmutableCredentials awsCredential = await _loginService.GetAppCredentialsAsync( );
var signer = new AWS4RequestSigner(awsCredential.AccessKey, awsCredential.SecretKey);
request = await signer.Sign(request, "execute-api", awsRegion);
return request;
}
This code works fine when I hard code credentials from one IAM user. But credentials retrieved from Cognito Identities its getting Forbidden error.
I did a test using SDK for S3 and I was able to successfully list the buckets with the same credentials received from Cognito Identities, but it is not possible to make requests on the API Gateway.
Can you help me? Where did I get lost?
I figure out what was going on.
After ensuring that the permissions settings were correct on AWS.
I found in the documentation that it is necessary to include in the HTTP header the token returned by the cognito (header name x-amz-security-token). So I changed the code to the following:
private async Task<HttpRequestMessage> BuildRequestAsync(string service)
{
var request = new HttpRequestMessage()
{
Method = HttpMethod.Get,
RequestUri = new Uri(baseURL + service)
};
ImmutableCredentials awsCredential = await _loginService.GetAppCredentialsAsync( );
//This where I add header to the HTTP request
request.Headers.Add("x-amz-security-token", awsCredential.Token);
var signer = new AWS4RequestSigner(awsCredential.AccessKey, awsCredential.SecretKey);
request = await signer.Sign(request, "execute-api", awsRegion);
return request;
}

Power BI API - How can I get reports from app.powerbi.com?

I have some client that uses the Power BI App.
And with this API I want to get all the reports (.pbix) to my folder.
How can I get them?
You can simply open the report in your browser and save it as .pbix file:
If you want to do this using an API, you will need Export Report In Group REST API. To use it, you need to acquire an access token and add it to your request header. You can acquire it by calling some of the AcuireToken methods from ADAL.
You can use code like this (please note there is no error checking in the example):
string clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; // Obtain at https://dev.powerbi.com/apps
string redirectUri = "https://login.live.com/oauth20_desktop.srf";
string resourceUri = "https://analysis.windows.net/powerbi/api";
string authorityUri = "https://login.windows.net/common/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authorityUri, new TokenCache()); // PM> Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory
var authenticationResult = await authContext.AcquireTokenAsync(resourceUri, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));
var accessToken = authenticationResult.AccessToken);
string powerBIApiUrl = "https://api.powerbi.com/v1.0/myorg/groups/{groupId}/reports/{reportKey}/Export"; // Replace groupId and reportKey with actual values
var request = WebRequest.Create(powerBIApiUrl) as HttpWebRequest;
request.KeepAlive = true;
request.Method = "GET";
request.ContentLength = 0;
request.ContentType = "application/json";
request.Headers.Add("Authorization", $"Bearer {accessToken}");
using (HttpWebResponse httpResponse = request.GetResponse() as System.Net.HttpWebResponse)
{
//Read httpResponse.GetResponseStream() to get the .pbix file
}

RestSharp response unauthorized

I am new to web based solutions. I am hitting a rest url using RestSharp library.
My code is as follows:
var cleint = new RestClient("http://REST_URL");
cleint.Authenticator = new HttpBasicAuthenticator("username", "password");
var request = new RestRequest();
request.Method = Method.GET;
request.Resource = "0.json";
IRestResponse response = cleint.Execute(request);
if (response != null && ((response.StatusCode == HttpStatusCode.OK) &&
(response.ResponseStatus == ResponseStatus.Completed)))
{
// var arr = JsonConvert.DeserializeObject<JArray> (response.Content);
}
The url returns a json file, when I hit it manually. But I want to use a C# console application to get the json file and save it to the disk. I am getting an unauthorized response when I run the above mentioned code:
response.ResponseStatus= "Unauthorized"
This is all it needed..
client.Authenticator = new NtlmAuthenticator();
So if your IIS settings have Windows Authentication set as enabled, this is what you are going to need, Http Basic authentication is not enough to by pass the server security

Mirror API add timeline item in offine

I have a mirror api app, and authorised users via outh and saved their access token and refresh token in database for offline use. Now I want to add timeline item to those users offline (using saved access token and refresh token).
Here is my code,
String accessToken = db.getAccessToken();
String refreshToken = db.getRefreshToken();
BatchRequest batch = MirrorClient.getMirror(null).batch();
BatchCallback callback = new BatchCallback();
TimelineItem notifyTimelineItem = new TimelineItem();
notifyTimelineItem.setHtml(ZONE_HTML);
Credential userCredential = AuthUtil.getCredential(userUniqueId);
userCredential.setAccessToken(accessToken);
userCredential.setRefreshToken(refreshToken);
MirrorClient.getMirror(userCredential).timeline().insert(notifyTimelineItem).queue(batch, callback);
batch.execute();
Here am getting error like Failed to insert item, user need to login . How can I add timeline item in offline?
Actually I got userCredential as null. So I used below code and it works fine.
String accessToken = db.getAccessToken();
Mirror service = new Mirror.Builder(new NetHttpTransport(), new GsonFactory(), null).setApplicationName("GOauthAndroid").build();
TimelineItem timelineItem = new TimelineItem();
timelineItem.setHtml(DANGER_ZONE_HTML);
timelineItem.setNotification(new NotificationConfig().setLevel("DEFAULT"));
try {
service.timeline().insert(timelineItem).setOauthToken(newAccessToken).execute();
} catch (IOException e) {
e.printStackTrace();
}

Authenticate with MS Crm Web Service

I'm looking for a way to authenticate a user (given a username and password) via the Microsoft CRM 4.0 Web Services API. Ideally, I'd like to filter down a list of projects based on which ones the logged in user has access to. i may be able to figure out the second part but I can't find a way to authenticate the user. The way all of the cals are currently made in the web service is via:
MyWebServices.CrmService svc = new MyWebServices.CrmService();
MyWebServices.CrmAuthenticationToken token = new MyWebServices.CrmAuthenticationToken();
token.OrganizationName = "MyCRM";
token.AuthenticationType = 0;
svc.CrmAuthenticationTokenValue = token;
svc.PreAuthenticate = true;
svc.Credentials = System.Net.CredentialCache.DefaultCredentials;
svc.Credentials = new NetworkCredential("hj", "mypass", "mydomain");
Then calls can be made via the service. I guess I could potentially try to authenticate to CRM via the user's username/password but it feels wrong somehow.
If you are in an on-premise environment, you should be able to use the following code to get a valid CRM service that can be used to retrieve your projects.
public static Microsoft.Crm.SdkTypeProxy.CrmService GetCrmService(string crmServerUrl, string organizationName, System.Net.NetworkCredential networkCredential)
{
// Setup the Authentication Token
CrmAuthenticationToken crmAuthenticationToken = new CrmAuthenticationToken
{
OrganizationName = organizationName,
AuthenticationType = 0
};
var crmServiceUriBuilder = new UriBuilder(crmServerUrl) { Path = "//MSCRMServices//2007//CrmService.asmx" };
// Instantiate a CrmService
var crmService = new Microsoft.Crm.SdkTypeProxy.CrmService
{
Url = crmServiceUriBuilder.ToString(),
UseDefaultCredentials = false,
Credentials = networkCredential,
CrmAuthenticationTokenValue = crmAuthenticationToken
};
return crmService;
}