Forum Discussion

seijim's avatar
seijim
Microsoft Employee
11 years ago
Solved

How to use Power BI Rest API without GUI authentication (redirect uri)

I'm developing an Azure worker role application which pushes data to PowerBI.com service.

I referred a C# sample client application via Git Hub. But it authenticates using GUI (redirect uri) during the below step.

>> token = authContext.AcquireToken(resourceUri, clientID, new Uri(redirectUri)).AccessToken.ToString();

 

I need a sample code without GUI (redirect uri) authentication which means silent mode authentication.

 

Thanks,

-Seiji

53 Replies

  • This is a very useful discussion, especially regarding the apps that my company develops, which are typically Windows services running on some headless datacenter server that doesn't usually have user interaction or a gui.  

     

    Consider a simple example: you have a service running that collects performance monitor stats from one or more servers and you want to ship those off via direct push to Power BI.  You might have a UI available when you installed the app, but it's not something that you'll be able to or want to return to in order to update a token.

     

    Many of the approaches mentioned below are good, but are now outdated:

     

    • AcquireTokenByRefreshToken() that aevers describes is no longer implemented in ADAL 3.
    • The user name/password AAD flow that jocaplan-MSFT mentions is no longer supported in ADAL3 (UserCredential class doesn't contain passwords any longer), although you can still access it by using ADAL 2.   However username/password can be problematic, because we've seen that AAD may require additional authenication steps (2FA, or smart cards in the case of Microsoft, etc.) which is I'm sure why MSFT dropped it from ADAL3.

    I may be missing some alternative approaches, so if there's a better way to handle authentication, please let me know.  I'm new to Power BI  :).

     

    In the ideal case, a Power BI app would require user authentication/acceptance to the rights requested at install time.  And from that point on, it would not require any additional user interaction via a GUI.  Users could terminate access by removing the application from their accepted application list.

     

    In the case of AAD applications, it functions pretty much like this.  Our app requires admin-level approval to add them to the organizational application list, but from that point on we can access AAD without further prompting.  If this was applied to Power BI, I'm sure there would be some additional work/thinking required because:

    • Just because an app was added to an organization doesn't imply that that app should have access to all Power BI workspaces.  Perhaps a workspace needs a token that can be supplied to external applications to use in an http request.
    • Sometimes having to request an admin to add an application to an organization can be troublesome, especially if the application is really only for one user's workspace. Ideally a user could allow access for a single user's application without admin interaction.

    So my ask in this message is to help me clear up my understanding on what is possible and the recommended approach for apps like mine today.  Also, want to raise this issue again now that the technology has evolved to see what if anything is on the roadmap.

    • heitzmanjared's avatar
      heitzmanjared
      Frequent Visitor

      ChrisWilliams I came here to post this exact thing. I can't find a simple way to "silent" authenticate because of these issues. Is there a standard approach for this now? Do we need to submit a feature request?

    • shaunwilks's avatar
      shaunwilks
      Helper V

      How did you go with this ? 

      Were you able to find a way to silentyly authenticate a user of the PowerBi service ?

       

      What was your solution to avoiding a mandatory GUI login ?

      • heitzmanjared's avatar
        heitzmanjared
        Frequent Visitor

        Shaun,

         

        There are a couple steps needed to silently authenticate.

         

        1. You need to manually log in with the account once because PowerBI will prompt you with permissions. Once you've accepted, the account can be set up for silent authentication.

         

        2. In c#, aquire the token like this

        // Create an instance of TokenCache to cache the access token
        TokenCache TC = new TokenCache();
        
        // Create an instance of AuthenticationContext to acquire an Azure access token
        authContext = new AuthenticationContext(authority, TC);
        
        string resourceUri = "https://analysis.windows.net/powerbi/api";
        string clientID = "your client id here";
        string email = "your email here";
        string password = "your password here";
        
        // Call AcquireToken to get an Azure token from Azure Active Directory token issuance endpoint
        token = authContext.AcquireToken(resourceUri, clientID, email, password).AccessToken;

         

        If you perform these steps properly, you can do whatever you want in the background. I have this in an executable file that's triggered by my database any time there is new data that I want to push out to a PBI dataset.

  • I did this by using the refresh token provided by the GUI authentication process and only using the AcquireTokenByRefreshToken method in my worker with the this token. I now just have to refresh the RefreshToken after a few days and update the configuration of my cloud worker (stored in redis -> worker checks for updated token). I do not re-use the Refresh Token in the configuration for every authentication request though but the new ones provided by AcquireTokenByRefreshToken.

    • seijim's avatar
      seijim
      Microsoft Employee

      Thanks for your suggestion.

      I will try it.

      • salesavlino's avatar
        salesavlino
        New Member

        Did the suggestion provided by him worked for you. Because i feel the suggestion provided is not relavant to the one you asked. He mentioned as  "using the refresh token provided by the GUI authentication process" but as per my understanding its not the question you asked. I guess asked not to redirect to the loginpage, instead it should authenticate silent through code and get the "code" parameter in turn.

    • markive's avatar
      markive
      Advocate II

      aevers what you say makes sense, but how do you refresh the token after a few days? Have you automated this?

       

      My app doesn't use Active Directory so I will want a single account setup and to silently authenticate to display the report to a certain set of users.

       

      I was thinking that the Active Directory REST api might be another option, but I've not used it before and just reading into it.

  • Below is the code for getting AccessToken by giving User credential, client Id (without AccessCode & Azure-Login UI).

     

    using Newtonsoft.Json;
    using System.IO;
    using System.Text;

     

    private async void SetAccessToken()
    {
    List<KeyValuePair<string, string>> vals = new List<KeyValuePair<string, string>>();
    vals.Add(new KeyValuePair<string, string>("grant_type", "password"));
    vals.Add(new KeyValuePair<string, string>("scope", "openid"));
    vals.Add(new KeyValuePair<string, string>("resource", "https://analysis.windows.net/powerbi/api"));
    vals.Add(new KeyValuePair<string, string>("client_id", ""));
    vals.Add(new KeyValuePair<string, string>("client_secret", ""));
    vals.Add(new KeyValuePair<string, string>("username", ""));
    vals.Add(new KeyValuePair<string, string>("password", ""));
    string TenantId = "";
    string url = string.Format("https://login.windows.net/{0}/oauth2/token", TenantId);
    HttpClient hc = new HttpClient();
    HttpContent content = new FormUrlEncodedContent(vals);
    HttpResponseMessage hrm = hc.PostAsync(url, content).Result;
    string responseData = "";
    if (hrm.IsSuccessStatusCode)
    {
    Stream data = await hrm.Content.ReadAsStreamAsync();
    using (StreamReader reader = new StreamReader(data, Encoding.UTF8))
    {
    responseData = reader.ReadToEnd();
    }
    }
    Token = JsonConvert.DeserializeObject<AccessToken>(responseData);
    }

     

    public class AccessToken
    {
    public string token_type;
    public string scope { get; set; }
    public string 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; }
    public string id_token { get; set; }
    }

    • spappuru's avatar
      spappuru
      New Member

      After executing the above code by replacing the values for ClientID,ClientSecret and TenantId, I am seeing the below error :

      Message: The remote server returned an error: (400) Bad Request.

      Status: ProtocolError.

       

      I have also tried using the authority uri as https://login.microsoftonline.com/<TenantID>/oauth2/token but still the same error.

       

      I really appreciate your response why I am seeing this bad request error.

       

    • VishvaPowerBI's avatar
      VishvaPowerBI
      Regular Visitor

      Hi,

      I am bit confused as to where I can call this code for test. I am new to C#  and it may be the reason for confusion. However I went thru some sample codes in sample application embed-a-tile-into-an-app (Github -dvana/PowerBI-CSharp) etc..  Any chance you could explain some dtails on how to use this code etc..

       

      Regards,

      Vish.

    • Phil_Seamark's avatar
      Phil_Seamark
      Microsoft Employee

      Hi there,

       

      What do you use for the Username/Password for your solution.  

       

      We use SSO at our company so not sure what account I can use.

       

       

      • heitzmanjared's avatar
        heitzmanjared
        Frequent Visitor

        I currently use my personal credentials. We're planning a change soon. What sucks is that PowerBI doesn't really allow multiple users to build reports off an API dataset. Our plan is to get a shared Service Account, pay for a license for the service account, and keep the password private to our group.

  • skaratela's avatar
    skaratela
    Frequent Visitor

    Hi all,

     

    with regards to the original subject, silent authenntication and also embedding PowerBI, i want to mention how i managed to get this to work and hopefully may help someone.

     

    The way i managed to get silent authentication to work for embedding report items into a custom application was to use a master powerBI account (created via Office 365) and then setup a Native App in Azure Active Directory (AAD) and make the master account be the owner of the Native App and set the correct permissions in the app and generate a Key.

     

    you then need to make a POST request to https://login.microsoftonline.com/common/oauth2/token with the following Keys and Values:

     

    KEYVALUE
    grant_typepassword
    scopeopenid
    resourcehttps://analysis.windows.net/powerbi/api
    client_id<the App ID for the Native App created in AAD>
    client_secret<the Key generated from the Native App created in AAD>
    username<Master PowerBI Account e.g. [email protected]>
    password<Password for the Master PowerBI account>

     

    You can test out your POST request by using the software application 'Postman'.

     

    This will return back a the access token.

     

    You then use the access token to pull back a Dashboard, Report or Tile - but bearing in mind that in PowerBI the Report / Dashboard must be bundelled into a PowerBI APP and owned by the Master Account - otherwise this won't work.

     

    <html>
    <script src="https://microsoft.github.io/PowerBI-JavaScript/demo/node_modules/jquery/dist/jquery.js"></script>
    <script src="https://microsoft.github.io/PowerBI-JavaScript/demo/node_modules/powerbi-client/dist/powerbi.js"></script>
    <script type="text/javascript">
    window.onload = function () {
    var embedConfiguration = {
        type: 'dashboard', //  dashboard
    	//change report embed url to dashboard embed url
        accessToken: 'TOKEN GENERATED FROM POST REQUEST GOES HERE',
        embedUrl: 'https://app.powerbi.com/dashboardEmbed?dashboardId=YOUR DASHBOARD ID - You can find this out by using http://docs.powerbi.apiary.io/ '  
    	}; 
    var $reportContainer = $('#dashboardContainer');
    var report = powerbi.embed($reportContainer.get(0), embedConfiguration);
    }
    
    function reloadreport(){
    	var element = $('#dashboardContainer');
    	alert(element);
    	var report = powerbi.get(element);
    	report.reload().catch(error => {console.log(error)  });
    };
    </script> 
    <div id="dashboardContainer"></div>
    </html>  
    
    

     

    Hope this helps someone.

     

    Shaheen K

    • dharamgoyal's avatar
      dharamgoyal
      Frequent Visitor

      Hi skaratela,

      I tried this https://login.microsoftonline.com/common/oauth2/token with param in post method but getting below error-

       

      "error": "invalid_grant",
      "error_description": "AADSTS65001: The user or administrator has not consented to use the application with ID 'xxxxxxxxxxxxxxxxxxxx' named 'LSNTestAPP'. Send an interactive authorization request for this user and resource.\r\nTrace ID: ac36a442-1603-479b-aad5-b8c75b0f4300\r\nCorrelation ID: 4cd0780a-2184-46ab-8d91-8a83987c5e72\r\nTimestamp: 2017-12-22 06:14:36Z",

      • skaratela's avatar
        skaratela
        Frequent Visitor
        Hi dharamgoyal it seems like perhaps the permissions in the Native Application in Azure haven't been set correctly. Also, are you using the same account for both the Native Application and also that same account when you are making the post request?

        Finally, have you bundelled your dashboard into a power BI application and made the owner of that app the same account as above?

        I know, it's so confusing.

        Cheers
    • jstearnes's avatar
      jstearnes
      Advocate I

      Hi sk I'm also getting the following error:

       

      {
      "error": "invalid_grant",
      "error_description": "AADSTS65001: The user or administrator has not consented to use the application with ID '3cc9615b-ed4c-436b-82a5-fb701c7e240d' named 'Launch BI'. Send an interactive authorization request for this user and resource.\r\nTrace ID: 46533754-26e3-43d9-9bea-2206d7ab3100\r\nCorrelation ID: c9e76852-19f3-4173-9866-5f914509fb7b\r\nTimestamp: 2018-01-16 17:31:30Z",
      "error_codes": [
      65001
      ],
      "timestamp": "2018-01-16 17:31:30Z",
      "trace_id": "46533754-26e3-43d9-9bea-2206d7ab3100",
      "correlation_id": "c9e76852-19f3-4173-9866-5f914509fb7b"
      }

       

      I believe I have followed your instructions, with the exception that I have created a WebApp as opposed to a Native app

      • jstearnes's avatar
        jstearnes
        Advocate I

        I just found the problem, the grant type needs to be "client_credentials" not "password"

  • skaratela's avatar
    skaratela
    Frequent Visitor

    Has anyone managed to do a silent auth using angular 4? 

  • Can we use POWERBI API without Azure AD. I have a reporting server