Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .NET Core/EncryptCredentials/EncryptCredentials.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ Global
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1A13B615-E49B-47EA-B23D-EE164230C682}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A13B615-E49B-47EA-B23D-EE164230C682}.Debug|Any CPU.Build.0 = Debug|Any CPU
Expand All @@ -31,4 +28,7 @@ Global
{1A13B615-E49B-47EA-B23D-EE164230C682}.Release|x86.ActiveCfg = Release|Any CPU
{1A13B615-E49B-47EA-B23D-EE164230C682}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ namespace EncryptCredentials.Controllers
{
using EncryptCredentials.Models;
using EncryptCredentials.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.PowerBI.Api.Models;
using Microsoft.Rest;
using System;

[Authorize(Policy = Startup.DatasourceAdministratorPolicy)]
[AutoValidateAntiforgeryToken]
public class EncryptCredentialsController : Controller
{
private readonly PowerBIService powerBIService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace EncryptCredentials.Controllers
{
using EncryptCredentials.Models;
using EncryptCredentials.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;

[Authorize(Policy = Startup.DatasourceAdministratorPolicy)]
public class HomeController : Controller
{
private readonly IOptions<AzureAd> azureAd;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ApplicationIcon />
<OutputType>Exe</OutputType>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.21.0" />
<PackageReference Include="Microsoft.PowerBI.Api" Version="3.21.0" />
<PackageReference Include="Microsoft.PowerBI.Core" Version="1.1.11" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.88.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="4.14.2" />
<PackageReference Include="Microsoft.PowerBI.Api" Version="4.22.0" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.76" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"EncryptCredentials": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
82 changes: 81 additions & 1 deletion .NET Core/EncryptCredentials/EncryptCredentials/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,27 @@ namespace EncryptCredentials
{
using EncryptCredentials.Models;
using EncryptCredentials.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Threading.Tasks;

public class Startup
{
public const string DatasourceAdministratorPolicy = "DatasourceAdministrator";
public const string DatasourceAdministratorRole = "PowerBI.DatasourceAdmin";
private const string OperatorChallengeScheme = "OperatorChallenge";

public Startup(IConfiguration configuration)
{
Configuration = configuration;
Expand All @@ -25,11 +38,77 @@ public Startup(IConfiguration configuration)
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("OperatorAzureAd"));

services.AddAuthentication()
.AddPolicyScheme(OperatorChallengeScheme, OperatorChallengeScheme, options =>
{
options.ForwardDefaultSelector = context =>
context.Request.Path.StartsWithSegments("/encryptcredential")
? CookieAuthenticationDefaults.AuthenticationScheme
: OpenIdConnectDefaults.AuthenticationScheme;
});

services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = OperatorChallengeScheme;
});

services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Events.OnRedirectToLogin = context =>
{
if (context.Request.Path.StartsWithSegments("/encryptcredential"))
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
}

context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
options.Events.OnRedirectToAccessDenied = context =>
{
if (context.Request.Path.StartsWithSegments("/encryptcredential"))
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
return Task.CompletedTask;
}

context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
});

var datasourceAdministratorPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireRole(DatasourceAdministratorRole)
.Build();

services.AddAuthorization(options =>
{
options.AddPolicy(DatasourceAdministratorPolicy, datasourceAdministratorPolicy);
options.FallbackPolicy = datasourceAdministratorPolicy;
});

services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
});

// Register AadService and PbiEmbedService for dependency injection
services.AddScoped(typeof(AadService))
.AddScoped(typeof(PowerBIService));

services.AddControllersWithViews();
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

// Loading appsettings.json in C# Model classes
services.Configure<AzureAd>(Configuration.GetSection("AzureAd"));
Expand All @@ -53,6 +132,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Licensed under the MIT license. -->
</head>

<body>
@Html.AntiForgeryToken()
<header class="col-lg-12 col-md-12 col-sm-12 shadow">
<div>
Encrypt Power BI Data Source Credentials
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
"PbiPassword": "",
"ClientSecret": ""
},
"OperatorAzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "",
"ClientId": "",
"ClientSecret": "",
"CallbackPath": "/signin-oidc"
},
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ $(function () {
// Freezing the contents for endpoint objects
Object.freeze(Endpoints);

$.ajaxSetup({
headers: {
"RequestVerificationToken": $("input[name='__RequestVerificationToken']").val()
}
});

// Cache constants
const ENABLED = "btn-primary";
const DISABLED = "btn-secondary";
Expand Down
17 changes: 12 additions & 5 deletions .NET Core/EncryptCredentials/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@

## Requirements

1. [.NET Core 3.1](https://aka.ms/netcore31) SDK or higher.
1. [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) SDK or higher.

2. IDE/code editor. We recommend using Visual Studio Code or Visual Studio 2019 (or a later version).
<br>
> **Note:** Visual Studio version >=16.5 is required to use .NET Core SDK 3.1.
2. IDE/code editor. We recommend using Visual Studio Code or Visual Studio 2022 (version 17.8 or later).


### Set up a Power BI app
### Set up the applications

Follow the steps on [aka.ms/EmbedForCustomer](https://aka.ms/embedforcustomer)

Create a separate Microsoft Entra app registration for users who operate this sample:

1. Add a web redirect URI for `https://localhost:5001/signin-oidc`.
2. Define an app role with the value `PowerBI.DatasourceAdmin` and allow users or groups as members.
3. Assign only the users or groups that are allowed to manage Power BI datasource credentials to that role.
4. Create a client secret and configure the tenant ID, client ID, and secret in the `OperatorAzureAd` section. Prefer environment variables, user secrets, or a secret store instead of writing the secret to `appsettings.json`.

The operator app registration authenticates and authorizes incoming users. Keep it separate from the privileged Power BI identity configured in the `AzureAd` section.

### Run the application on localhost

1. Open the [EncryptCredentials.sln](./EncryptCredentials.sln) file in Visual Studio. If you are using Visual Studio Code, open [EncryptCredentials](./EncryptCredentials) folder.
Expand Down