Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Earn the coveted Fabric Analytics Engineer certification. 100% off your exam for a limited time only!

Reply
MattCalderwood
Responsive Resident
Responsive Resident

'Generate Token' API returns token with variable (short) expirations: Using Service Principal

Hi All.

I am migrating an application over to using Service Principal accounts with the Power BI REST API.
This works great - but then I noticed that embedded reports would sometimes stop working very quickly - with 401 (unauthorized) errors recorded in the browser dev-tools/console.

I am using the 'Generate Token' API endpoint as described here: https://docs.microsoft.com/en-us/rest/api/power-bi/embedtoken/generatetoken

This is accessed by using an Azure access token generated via the Microsoft.IdentityModel.Clients.ActiveDirectory library, which follows the Service Principal authentication flow in .Net Core.

So what's the problem?
The Azure AD Access token gets cached by the Identity Model Client - saving excessive calls to Azure (a good thing), however, when calling the Power BI REST API with this token - the resulting Embedding token gets an expiration that matches the original AD access token.

These AD tokens last an hour - and will get cached for that duration.
This means that at any time (depending on when your AD tokens are cached), you can end up with an embedding token that is only usable for a few seconds!

Expected behaviour:
Even if an AD token only has a couple of minutes before it expires - the Power BI API should issue embedding tokens with an expiration of a full hour.

Is anyone else seeing this behaviour?
Does this need to be escalated as a support call?

Thanks,
Matt

1 ACCEPTED SOLUTION

Hi @basvanleeuwen 

I ended up taking a different route in the end, adding details here for others...

The C# flow for creating an Authentication Context and passing in details for the Service Principal, results in the Azure auth token being cached within your application/api. After the first request, this cached token is then used for approximately 55 minutes (as you had noticed) - but you can manipulate this cache in code.

C# even lets you handle the serialization of these tokens in cases where you don't want to use the built-in cache.
https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/Token-cache-serialization

I didn't go the full custom serialization route, instead I opted for a simple cache invalidation method.

private async Task<AuthenticationResult> CallAuthTokenEndpoint()
{
    var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/...");
    var credential = new ClientCredential("abcd-1234-your-auth-client-id", "your-client-secret");

    ClearExpiredTokenCache(authenticationContext);

    return await authenticationContext.AcquireTokenAsync("https://analysis.windows.net/powerbi/api", credential);
}

private void ClearExpiredTokenCache(AuthenticationContext authenticationContext)
{
    var cachedItems = authenticationContext.TokenCache.ReadItems();
    var matchedItem = cachedItems.FirstOrDefault(tokenCacheItem =>
        string.Compare(tokenCacheItem.ClientId, "abcd-1234-your-auth-client-id",
            StringComparison.OrdinalIgnoreCase) == 0
        && string.Compare(tokenCacheItem.Resource, "https://analysis.windows.net/powerbi/api",
            StringComparison.OrdinalIgnoreCase) == 0);

    if (matchedItem != null)
    {
        var expireIfAfter =
            DateTime.UtcNow.AddMinutes(50);

        if (expireIfAfter > matchedItem.ExpiresOn)
        {
            authenticationContext.TokenCache.DeleteItem(matchedItem);
        }
    }
}

CallAuthTokenEndpoint => Is responsible for getting the Azure Auth Token, which will then be used by the Power BI API.

The call to AquireTokenAsync in this method, will return from the local cache (if a token has been generated within the last 55 minutes). So before calling this method, we can do our own clearing of the token cache - via the Authentication Context.

ClearExpiredTokenCache => fetches all items from the local cache. Then finds the one that matches our ClientId and desired resource (analysis.windows.net/powerbi/api).

If we find a matching token... then we can choose to clear it from the cache IF more than 10 minutes have passed. (Utc.Now + 50 mins).


With no token in the cache, the call to AquireTokenAsync will always generate a new token with another 60 minutes on the clock.

This obviously results in increased frequency of calls to Azure for auth tokens... but it gives you more flexibility for manging token durations.

I am assuming that the Power BI token expiration being in-sync with the expiration of the Azure AD token, is by design.
Hopefully one of our suggestions will help other people in similar situations,

Cheers, Matt

 

View solution in original post

2 REPLIES 2
basvanleeuwen
New Member

Yes, indeed @MattCalderwood,  we are facing the exact same behaviour. The expiration of the token remains the same upon each new token-request, which doesn't seem to be normal.

 

However, It looks like (approx.) 5 minutes before the expiration a new token can be requested which will give a new expiration time. We were able to handle this with a timer (within our SPA) that ends 5 minutes before the tokens' expiration, after which it will fetch a new token with a fresh expirationtime.

 

 

 

 

 

Hi @basvanleeuwen 

I ended up taking a different route in the end, adding details here for others...

The C# flow for creating an Authentication Context and passing in details for the Service Principal, results in the Azure auth token being cached within your application/api. After the first request, this cached token is then used for approximately 55 minutes (as you had noticed) - but you can manipulate this cache in code.

C# even lets you handle the serialization of these tokens in cases where you don't want to use the built-in cache.
https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/Token-cache-serialization

I didn't go the full custom serialization route, instead I opted for a simple cache invalidation method.

private async Task<AuthenticationResult> CallAuthTokenEndpoint()
{
    var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/...");
    var credential = new ClientCredential("abcd-1234-your-auth-client-id", "your-client-secret");

    ClearExpiredTokenCache(authenticationContext);

    return await authenticationContext.AcquireTokenAsync("https://analysis.windows.net/powerbi/api", credential);
}

private void ClearExpiredTokenCache(AuthenticationContext authenticationContext)
{
    var cachedItems = authenticationContext.TokenCache.ReadItems();
    var matchedItem = cachedItems.FirstOrDefault(tokenCacheItem =>
        string.Compare(tokenCacheItem.ClientId, "abcd-1234-your-auth-client-id",
            StringComparison.OrdinalIgnoreCase) == 0
        && string.Compare(tokenCacheItem.Resource, "https://analysis.windows.net/powerbi/api",
            StringComparison.OrdinalIgnoreCase) == 0);

    if (matchedItem != null)
    {
        var expireIfAfter =
            DateTime.UtcNow.AddMinutes(50);

        if (expireIfAfter > matchedItem.ExpiresOn)
        {
            authenticationContext.TokenCache.DeleteItem(matchedItem);
        }
    }
}

CallAuthTokenEndpoint => Is responsible for getting the Azure Auth Token, which will then be used by the Power BI API.

The call to AquireTokenAsync in this method, will return from the local cache (if a token has been generated within the last 55 minutes). So before calling this method, we can do our own clearing of the token cache - via the Authentication Context.

ClearExpiredTokenCache => fetches all items from the local cache. Then finds the one that matches our ClientId and desired resource (analysis.windows.net/powerbi/api).

If we find a matching token... then we can choose to clear it from the cache IF more than 10 minutes have passed. (Utc.Now + 50 mins).


With no token in the cache, the call to AquireTokenAsync will always generate a new token with another 60 minutes on the clock.

This obviously results in increased frequency of calls to Azure for auth tokens... but it gives you more flexibility for manging token durations.

I am assuming that the Power BI token expiration being in-sync with the expiration of the Azure AD token, is by design.
Hopefully one of our suggestions will help other people in similar situations,

Cheers, Matt

 

Helpful resources

Announcements
April AMA free

Microsoft Fabric AMA Livestream

Join us Tuesday, April 09, 9:00 – 10:00 AM PST for a live, expert-led Q&A session on all things Microsoft Fabric!

March Fabric Community Update

Fabric Community Update - March 2024

Find out what's new and trending in the Fabric Community.