Forum Discussion
Headless authentication for API calls
I've had the same issue recently and have had to switch to using a device code. I've just dumped a copy of my code below.
There are a few steps required.
1 - Run it initially to get a device code. You will have to go to a verification url and enter a code. This then authenticates the app.
2 - Serialise the token cache from the authentication token.
Next time through, create a token cache from the saved state and you can log back in.
It all feels a bit hacky, but appears to work ok. I'm planning on creating a simple github project with all of this in, but am still debugging some issues at the moment
public PowerBIClient(string clientId, string key, HttpClient httpClient, ILogger log, byte[] state)
{
Log = log;
ClientId = clientId;
_key = key;
_httpClient = httpClient;
var tokenCache = state == null ? new TokenCache() : new TokenCache(state);
_authenticationContext = new AuthenticationContext(AuthorityUri, tokenCache);
}
#pragma warning disable RCS1163 // Unused parameter.
private async Task<string> GetAccessTokenAsync(CancellationToken ct)
#pragma warning restore RCS1163 // Unused parameter.
{
if (_accessToken == null)
{
if (_authenticationContext.TokenCache.Count > 0)
{
try
{
var result = await _authenticationContext.AcquireTokenSilentAsync(ResourceUri, ClientId).ConfigureAwait(false);
_accessToken = result.AccessToken;
Log.Debug("Got cached access token until {expire} ({duration})",
result.ExpiresOn, result.ExpiresOn - DateTimeOffset.UtcNow);
return _accessToken;
}
catch (Exception ex)
{
Log.Warning("Failed to acquire token from cached {exception}", ex.Message);
}
}
}
if (_accessToken == null)
{
var codeResult = await _authenticationContext.AcquireDeviceCodeAsync(ResourceUri, ClientId).ConfigureAwait(false);
Log.Warning("Need authentication from {location} {code}", codeResult.VerificationUrl, codeResult.UserCode);
var result = await _authenticationContext.AcquireTokenByDeviceCodeAsync(codeResult).ConfigureAwait(false);
Log.Debug("Got new access token until {expire} ({duration})",
result.ExpiresOn, result.ExpiresOn - DateTimeOffset.UtcNow);
_accessToken = result.AccessToken;
}
else
{
var result = await _authenticationContext.AcquireTokenSilentAsync(ResourceUri, ClientId).ConfigureAwait(false);
Log.Debug("Got refreshed access token until {expire} ({duration})",
result.ExpiresOn, result.ExpiresOn - DateTimeOffset.UtcNow);
_accessToken = result.AccessToken;
}
return _accessToken;
}
Hi njrandell
Are you able to supply some more detail? I probably need some more info to get these functions working.
Cheers,
Phil
- njrandell9 years agoHelper I
Have a look at https://github.com/sceneskope/powerbi
Admitedly the documentation isn't great, but I've had this working for the last few months and am happy to make it clearer if needed.
- Anonymous9 years agoNot applicable
I got my authentication running headless with passing a UserCredential instance to the AuthContext. Therefor I provide username and password. See also: https://github.com/Microsoft/PowerBI-Developer-Samples/blob/master/App%20Owns%20Data/PowerBIEmbedded_AppOwnsData/Controllers/HomeController.cs
They are using a UserPasswordCredential object, I am not aware of the differences of those two classes. But both should work headless.
- njrandell9 years agoHelper I
I'm using .Net core, and last time I checked those libraries don't support username and passwords. In fact I remember seeing a comment on github from one of the developers saying they won't add in username and passwords.
I'm using the libraries I created on github to push into power bi from a number of places, such as a raspberry pi running a dotnet core app, all the way through to a service fabric service running .net 452.
If you aren't using .Net core, then you are ok with the UserPasswordCredential object.