Row Level Security for Embedded PowerBI Reports with Service Principal Authentication
To present a PowerBI report user or consumer with a securely pre-filtered dataset, row level security must be used. In a PowerBI embedded architecture where “app owns data”, implementing row level security (RLS) requires a modification to the token generation request. By specifying a role and user in the token request, we can generate an embed token specific to the user’s data access.
Overview
The application of RLS to PowerBI in an embedded scenario requires a 2-part change from the vanilla embed scenario. With the goal of embedding a report with row level security enforced, we must make changes to the embed token generation as well as to the report in PowerBI desktop.
Obtaining a User/Role-Specific Embed Token
We’re going to start from the .Net Core example for generating an embed token (Blog Post | Repository). At line 61 we adjust the GenerateTokenRequest function to take in a new parameter, an EffectiveIdentity. Our GenerateTokenRequest goes from:
var generateTokenRequestParameters = new GenerateTokenRequest(accessLevel: "view");
to:
var generateTokenRequestParameters = new GenerateTokenRequest( "View", // access level null, identities: new List<EffectiveIdentity> { new EffectiveIdentity( username: "PutTheUserNameHere", roles: new List<string> { "RoleNameHereThisIsDefinedPerReport"}, datasets: new List<string> { report.DatasetId } )} );
If your code (like the sample) does not already include list structures, you will need to add a reference to System.Collections.Generic with this adjustment. Other necessary changes to your code include likely input parameters for the userID of the runtime user, potentially the role(s) as well.
Preparing a Report for Row Level Security in PowerBI Desktop
Without diving too far into the structure of your data, we’re going to play with a sample model so simple that it is a single table. This model fulfills the most important constraint for row level security – a field that can be used in a DAX expression to validate access by user or by role – in this case by including a single column for the user’s identifier.
Back in the PowerBI report designer view under the Modeling tab, we find an option for Manage Roles. We are creating a single role called “Users” and it filters the single table in our dataset with the expression [USERID] = username(), that is the column USERID must equal the username.
Note! When you view the report in PowerBI desktop and a role is not applied to your user, RLS is not enforced. The view-as functionality can be useful to apply the RLS of a specific role. In the example below, no rows match the username ‘DSK-LAPTOP\drewk’ when the role ‘Users’ is applied.
Tip! Want to know what username is being used at report runtime? Add a measure to your dataset for RUNTIMEUSERID = username()
and add the measure to a card visual.
The Resulting Row Level Security
A GUID-style username is passed to the report at runtime along with the role name ‘Users’, matching a subset of the rows. The resulting dataset and report embed token have RLS enforced, displaying only 57 of the total 114 rows.
References
https://docs.microsoft.com/en-us/power-bi/developer/embedded-row-level-security#applying-user-and-role-to-an-embed-token
https://docs.microsoft.com/en-us/power-bi/service-admin-rls#using-the-username-or-userprincipalname-dax-function
https://docs.microsoft.com/en-us/dotnet/api/microsoft.powerbi.api.v2.models.effectiveidentity?view=azure-dotnet
https://docs.microsoft.com/en-us/dotnet/api/microsoft.powerbi.api.v2.models.report?view=azure-dotnet
https://docs.microsoft.com/en-us/power-bi/service-admin-rls