Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
12 changes: 6 additions & 6 deletions .github/workflows/azure-webapps-dotnet-core.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@

name: Build and deploy to Azure Web App

env:
AZURE_WEBAPP_NAME: bff-angular-aspnetcore # set this to the name of your Azure Web App
AZURE_WEBAPP_PACKAGE_PATH: '.' # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: '9.0' # set this to the .NET Core version to use
AZURE_WEBAPP_NAME: bff-angular-aspnetcore # set this to the name of your Azure Web App
AZURE_WEBAPP_PACKAGE_PATH: "." # set this to the path to your web app project, defaults to the repository root
DOTNET_VERSION: "9.0" # set this to the .NET Core version to use

on:
push:
branches: [ "deploy" ]
branches: ["deploy"]
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -56,14 +55,15 @@ jobs:
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/myapp
include-hidden-files: true # otherwise .well-known folder is not included

deploy:
permissions:
contents: none
runs-on: ubuntu-latest
needs: build
environment:
name: 'Development'
name: "Development"
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@

name: .NET and npm build

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]

jobs:
build:
runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v5
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,6 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml

# wwwroot (as it gets recreated on every npm run build)
**/wwwroot/*
62 changes: 32 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ The ASP.NET Core project is setup to run in development and production. In produ
Configure the YARP reverse proxy to match the Angular CLI URL. This is only required in development. I always use HTTPS in development and the port needs to match the Angular CLI developement env.

> [!IMPORTANT]
> In a real Angular project, the additional dev routes need to be added so that the __dev refresh__ works!
> In a real Angular project, the additional dev routes need to be added so that the **dev refresh** works!

```json
"UiDevServerUrl": "https://localhost:4201",
"ReverseProxy": {
{
"UiDevServerUrl": "https://localhost:4201",
"ReverseProxy": {
"Routes": {
"assets": {
"ClusterId": "cluster1",
Expand Down Expand Up @@ -79,14 +80,18 @@ Configure the YARP reverse proxy to match the Angular CLI URL. This is only requ
"Match": {
"Path": "/{nomatterwhat}.js.map"
}
},
"wellknown": {
"ClusterId": "cluster1",
"Match": {
"Path": ".well-known/{**catch-all}"
}
}
},
"Clusters": {
"cluster1": {
"HttpClient": {
"SslProtocols": [
"Tls12"
]
"SslProtocols": ["Tls12"]
},
"Destinations": {
"cluster1/destination1": {
Expand All @@ -96,52 +101,52 @@ Configure the YARP reverse proxy to match the Angular CLI URL. This is only requ
}
}
}
}
```

## Setup Angular CLI

Add the certificates to the CLI project for example in the **/certs** folder
Add the certificates to the CLI project for example in the **/certs** folder.

Update the Angular CLI angular.json file:
Update the Angular CLI `angular.json` file:

```json
...
"serve": {
"builder": "@angular/build:dev-server",
"options": {
"sslKey": "certs/dev_localhost.key",
"sslCert": "certs/dev_localhost.pem",
"port": 4201,
},
"builder": "@angular/build:dev-server",
"options": {
"sslKey": "certs/dev_localhost.key",
"sslCert": "certs/dev_localhost.pem",
"port": 4201
}
}
...
```

> [!NOTE]
> The default Angular setup uses port 4200, this needs to match the YARP reverse proxy settings for development.

Update the outputPath for the (angular cli build) to deploy the production paths to the wwwroot of the .NET project
Update the `outputPath` for the (Angular CLI build) to deploy the production paths to the `wwwroot` of the .NET project

```
"architect": {
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"outputPath": {
"outputPath": {
"base": "../server/wwwroot",
"browser": ""
},
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
]
"styles": ["src/styles.css"]
},
```

Expand Down Expand Up @@ -219,7 +224,7 @@ Add the Azure App registration settings to the **appsettings.Development.json**
},
```

App Service (linux plan) configuration
App Service (linux plan) configuration

```
MicrosoftEntraID__Instance --your-value--
Expand Down Expand Up @@ -253,24 +258,22 @@ Or just open Visual Studio and run the solution.
Github actions is used for the DevOps. The build pipeline builds both the .NET project and the Angular CLI project using npm. The two projects are built in the same step because the UI project is built into the wwwroot of the server project.

```yaml

name: .NET and npm build

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]

jobs:
build:
runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 9.0.x

Expand All @@ -289,7 +292,6 @@ jobs:
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal

```

## github actions Azure deployment
Expand Down
1 change: 1 addition & 0 deletions server/BffMicrosoftEntraID.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.10" NoWarn="NU1605" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.10" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageReference Include="Microsoft.Identity.Web.GraphServiceClient" Version="4.0.1" />
<PackageReference Include="Microsoft.Identity.Web" Version="4.0.1" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="4.0.1" />
Expand Down
1 change: 0 additions & 1 deletion server/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public ActionResult Login(string? returnUrl, string? claimsChallenge)
}

// [ValidateAntiForgeryToken] // not needed explicitly due the the Auto global definition.
[IgnoreAntiforgeryToken] // need to apply this to the form post request
[Authorize]
[HttpPost("Logout")]
public IActionResult Logout()
Expand Down
2 changes: 1 addition & 1 deletion server/Pages/Error.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<title>Error</title>
</head>

Expand Down
7 changes: 5 additions & 2 deletions server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
serverOptions.AddServerHeader = false;
});

builder.Services.AddOpenApi();

var services = builder.Services;
var configuration = builder.Configuration;

Expand Down Expand Up @@ -49,8 +51,8 @@
// If you use persistent cache, you do not require this.
// You can also return the 403 with the required scopes, this needs special handling for ajax calls
// The check is only for single scopes
services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme,
options => options.Events = new RejectSessionCookieWhenAccountNotInCacheEvents(initialScopes));
services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme,
options => options.Events = new RejectSessionCookieWhenAccountNotInCacheEvents(initialScopes));

services.AddControllersWithViews(options =>
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));
Expand All @@ -74,6 +76,7 @@

app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
app.MapOpenApi();
}
else
{
Expand Down
10 changes: 7 additions & 3 deletions server/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,18 @@
"Match": {
"Path": "/{nomatterwhat}.js.map"
}
},
"wellknown": {
"ClusterId": "cluster1",
"Match": {
"Path": ".well-known/{**catch-all}"
}
}
},
"Clusters": {
"cluster1": {
"HttpClient": {
"SslProtocols": [
"Tls12"
]
"SslProtocols": ["Tls12"]
},
"Destinations": {
"cluster1/destination1": {
Expand Down
Loading