diff --git a/.github/workflows/BuildAndPack.yml b/.github/workflows/BuildAndPack.yml
index c375039..7a12a68 100644
--- a/.github/workflows/BuildAndPack.yml
+++ b/.github/workflows/BuildAndPack.yml
@@ -39,8 +39,9 @@ jobs:
dotnet-version: |
2.1.x
3.0.x
+ 3.1.x
6.0.x
- 8.0.303
+ 8.0.401
- name: Cache .nuke/temp, ~/.nuget/packages
uses: actions/cache@v2
with:
@@ -68,8 +69,9 @@ jobs:
dotnet-version: |
2.1.x
3.0.x
+ 3.1.x
6.0.x
- 8.0.303
+ 8.0.401
- name: Cache .nuke/temp, ~/.nuget/packages
uses: actions/cache@v2
with:
@@ -97,8 +99,9 @@ jobs:
dotnet-version: |
2.1.x
3.0.x
+ 3.1.x
6.0.x
- 8.0.303
+ 8.0.401
- name: Cache .nuke/temp, ~/.nuget/packages
uses: actions/cache@v2
with:
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 69ebe2a..0dc54a7 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -1,57 +1,85 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
- "title": "Build Schema",
- "$ref": "#/definitions/build",
+ "properties": {
+ "Configuration": {
+ "type": "string",
+ "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
+ "enum": [
+ "Debug",
+ "Release"
+ ]
+ },
+ "GithubToken": {
+ "type": "string"
+ },
+ "NuGetToken": {
+ "type": "string"
+ },
+ "Solution": {
+ "type": "string",
+ "description": "Path to a solution file that is automatically loaded"
+ }
+ },
"definitions": {
- "build": {
- "type": "object",
+ "Host": {
+ "type": "string",
+ "enum": [
+ "AppVeyor",
+ "AzurePipelines",
+ "Bamboo",
+ "Bitbucket",
+ "Bitrise",
+ "GitHubActions",
+ "GitLab",
+ "Jenkins",
+ "Rider",
+ "SpaceAutomation",
+ "TeamCity",
+ "Terminal",
+ "TravisCI",
+ "VisualStudio",
+ "VSCode"
+ ]
+ },
+ "ExecutableTarget": {
+ "type": "string",
+ "enum": [
+ "Clean",
+ "Compile",
+ "Pack",
+ "PushToNuGet",
+ "Restore",
+ "Test"
+ ]
+ },
+ "Verbosity": {
+ "type": "string",
+ "description": "",
+ "enum": [
+ "Verbose",
+ "Normal",
+ "Minimal",
+ "Quiet"
+ ]
+ },
+ "NukeBuild": {
"properties": {
- "Configuration": {
- "type": "string",
- "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
- "enum": [
- "Debug",
- "Release"
- ]
- },
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
- "GithubToken": {
- "type": "string"
- },
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
- "type": "string",
"description": "Host for execution. Default is 'automatic'",
- "enum": [
- "AppVeyor",
- "AzurePipelines",
- "Bamboo",
- "Bitrise",
- "GitHubActions",
- "GitLab",
- "Jenkins",
- "Rider",
- "SpaceAutomation",
- "TeamCity",
- "Terminal",
- "TravisCI",
- "VisualStudio",
- "VSCode"
- ]
+ "$ref": "#/definitions/Host"
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
- "NuGetToken": {
- "type": "string"
- },
"Partition": {
"type": "string",
"description": "Partition to use on CI"
@@ -75,47 +103,22 @@
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
- "type": "string",
- "enum": [
- "Clean",
- "Compile",
- "Pack",
- "PushToNuGet",
- "Restore",
- "Test"
- ]
+ "$ref": "#/definitions/ExecutableTarget"
}
},
- "Solution": {
- "type": "string",
- "description": "Path to a solution file that is automatically loaded"
- },
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
- "type": "string",
- "enum": [
- "Clean",
- "Compile",
- "Pack",
- "PushToNuGet",
- "Restore",
- "Test"
- ]
+ "$ref": "#/definitions/ExecutableTarget"
}
},
"Verbosity": {
- "type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",
- "enum": [
- "Minimal",
- "Normal",
- "Quiet",
- "Verbose"
- ]
+ "$ref": "#/definitions/Verbosity"
}
}
}
- }
-}
\ No newline at end of file
+ },
+ "$ref": "#/definitions/NukeBuild"
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22324a1..b7ede73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
# Changelog
+## [v1.0.0-preview.01]
+
+Features:
+
+* Allow configuring "named" policies, and applying different policies to different endpoints #172, #173, #185
+* Allow customizing the `HeaderPolicyCollection` just before it is applied, customizing per request #174, #185
+* Make adding directives to `Content-Security-Policy` idempotent to avoid duplicates #169
+* Add `AddDefaultApiSecurityHeaders()` for adding default headers to APIs #183, #184
+* Add `AddPermissionsPolicyWithRecommendedDirectives()` and `PermissionsPolicyBuilder.AddDefaultSecureDirectives()` for adding secure `Permissions-Policy` directives in bulk #183, #184
+* NetEscapades.AspNetCore.SecurityHeaders now has an icon, thanks @khalidabuhakmeh! #195
+
+Breaking Changes:
+
+* Drop support for .NET Standard 2.0, raises minimum framework to .NET Core 3.1 #167, #171
+* Removed "document header" functionality, in favour of always adding all headers #186
+* Remove `X-XSS-Protection` from default headers and mark obsolete #168
+* Add `cross-origin-opener-policy: same-origin` to default headers #184
+* Mark `Feature-Policy` as obsolete #187
+* Mark `Expect-CT` as obsolete #197
+* Make nonce generation lazy on call to `HttpContext.GetNonce()` #198
+
## [v0.24.0]
Features:
diff --git a/Directory.Build.props b/Directory.Build.props
index 6bed1b0..7798534 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -14,11 +14,16 @@
git
embedded
false
- true
latest
false
+ README.md
+ icon.png
-
+
+
+
+
+
\ No newline at end of file
diff --git a/NetEscapades.AspNetCore.SecurityHeaders.sln b/NetEscapades.AspNetCore.SecurityHeaders.sln
index b9cafaa..8a68c4b 100644
--- a/NetEscapades.AspNetCore.SecurityHeaders.sln
+++ b/NetEscapades.AspNetCore.SecurityHeaders.sln
@@ -6,6 +6,14 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F51609AF-585F-4BBD-89BD-4FE245292018}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7EBFA813-EE77-4377-8A45-E5A8527CC12C}"
+ ProjectSection(SolutionItems) = preProject
+ .github\workflows\BuildAndPack.yml = .github\workflows\BuildAndPack.yml
+ CHANGELOG.md = CHANGELOG.md
+ Directory.Build.props = Directory.Build.props
+ README.md = README.md
+ releasenotes.props = releasenotes.props
+ version.props = version.props
+ EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A2E64919-761D-4412-AFF7-D167023FD55E}"
EndProject
diff --git a/README.md b/README.md
index 374afb6..77e6407 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-# NetEscapades.AspNetCore.SecurityHeaders
+#
NetEscapades.AspNetCore.SecurityHeaders
-[](https://ci.appveyor.com/project/andrewlock/netescapades-aspnetcore-securityheaders/branch/master)
-
+
[](https://www.nuget.org/packages/NetEscapades.AspNetCore.SecurityHeaders/)
-[](http://myget.org/gallery/acndrewlock-ci)
+
+
A small package to allow adding security headers to ASP.NET Core websites
@@ -18,7 +18,7 @@ PM> Install-Package NetEscapades.AspNetCore.SecurityHeaders
Or using the `dotnet` CLI
```bash
-dotnet add package NetEscapades.AspNetCore.SecurityHeaders
+dotnet add package NetEscapades.AspNetCore.SecurityHeaders --version 1.0.0-preview.1
```
## Usage
@@ -33,13 +33,17 @@ When you install the package, it should be added to your `.csproj`. Alternativel
-
+
```
-Simply add the middleware to your ASP.NET Core application by configuring it as part of your normal `Startup` pipeline. Note that the order of middleware matters, so to apply the headers to all requests it should be configured first in your pipeline.
+There are various ways to configure the headers for your application.
+
+In the simplest scenario, add the middleware to your ASP.NET Core application by configuring it as part of your normal `Startup` pipeline (or `WebApplication` in .NET 6+).
+
+> Note that the order of middleware matters, so to apply the headers to all requests it should be configured first in your pipeline.
To use the default security headers for your application, add the middleware using:
@@ -56,16 +60,12 @@ This adds the following headers to all responses that pass through the middlewar
* `X-Content-Type-Options: nosniff`
* `Strict-Transport-Security: max-age=31536000; includeSubDomains` - _only applied to HTTPS responses_
-* `X-Frame-Options: Deny` - _only applied to "document" responses_
-* `X-XSS-Protection: 1; mode=block` - _only applied to "document" responses_
-* `Referrer-Policy: strict-origin-when-cross-origin` - _only applied to "document" responses_
-* `Content-Security-Policy: object-src 'none'; form-action 'self'; frame-ancestors 'none'` - _only applied to "document" responses_
-
-"Document" responses are defined as responses that return one of the following content-types:
+* `X-Frame-Options: Deny`
+* `Referrer-Policy: strict-origin-when-cross-origin`
+* `Content-Security-Policy: object-src 'none'; form-action 'self'; frame-ancestors 'none'`
+* `Cross-Origin-Opener-Policy: same-origin`
-- `text/html`
-- `text/javascript`
-- `application/javascript`
+Note that these policies represent a "safe" set of minimum defaults that should be valid for most sites, but are not the most secure they could be. You are advised to think about what features you need, and to restrict them where possible. For example, a stronger [Content Security Policy](#addcontentsecuritypolicy) should be used where possible, as well as a [Permissions Policy](#addpermissionspolicy).
## Customising the security headers added to responses
@@ -76,7 +76,6 @@ public void Configure(IApplicationBuilder app)
{
var policyCollection = new HeaderPolicyCollection()
.AddFrameOptionsDeny()
- .AddXssProtectionBlock()
.AddContentTypeOptionsNoSniff()
.AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 60 * 60 * 24 * 365) // maxage = one year in seconds
.AddReferrerPolicyStrictOriginWhenCrossOrigin()
@@ -87,18 +86,7 @@ public void Configure(IApplicationBuilder app)
builder.AddFormAction().Self();
builder.AddFrameAncestors().None();
})
- .AddCrossOriginOpenerPolicy(builder =>
- {
- builder.SameOrigin();
- })
- .AddCrossOriginEmbedderPolicy(builder =>
- {
- builder.RequireCorp();
- })
- .AddCrossOriginResourcePolicy(builder =>
- {
- builder.SameOrigin();
- })
+ .AddCrossOriginOpenerPolicy(x => x.SameOrigin());
.AddCustomHeader("X-My-Test-Header", "Header value");
app.UseSecurityHeaders(policyCollection);
@@ -152,6 +140,173 @@ public void Configure(IApplicationBuilder app)
}
```
+## Applying different headers to different endpoints
+
+In some situations, you may need to apply different security headers to different endpoints. For example, you may want to have a very restrictive Content-Security-Policy by default, but then have a more relaxed on specific endpoints that require it. This is supported, but requires more configuration.
+
+### 1. Configure your policies using `AddSecurityHeaderPolicies()`
+
+You can configure named and default policies by calling `AddSecurityHeaderPolicies()` on `IServiceCollection`. You can configure the default policy to use, as well as any named policies. For example, the following configures the default policy (used for all requests that are not customised for an endpoint), and a named policy:
+
+```csharp
+var builder = WebApplication.CreateBuilder();
+
+// 👇 Call AddSecurityHeaderPolicies()
+builder.Services.AddSecurityHeaderPolicies()
+ .SetDefaultPolicy(policy => policy.AddDefaultSecurityHeaders()) // 👈 Configure the default policy
+ .AddPolicy("CustomPolicy", policy => policy.AddCustomHeader("X-Custom", "SomeValue")); // 👈 Configure named policies
+
+```
+
+### 2. Call `UseSecurityHeaders()` early in the middleware pipeline
+
+The security headers middleware can only add headers to _all_ requests if it is early in the middleware pipeline, so it's important to add the headers middleware at the start of your middleware pipeline by calling `UseSecurityHeaders()`. For example:
+
+```csharp
+var builder = WebApplication.CreateBuilder();
+
+// 👇 Configure policies as shown previously
+builder.Services.AddSecurityHeaderPolicies()
+ .SetDefaultPolicy(policy => policy.AddDefaultSecurityHeaders())
+ .AddPolicy("CustomPolicy", policy => policy.AddCustomHeader("X-Custom", "SomeValue"));
+
+var app = builder.Build();
+
+// Add the middleware to the start of your pipeline
+// 👇
+app.UseSecurityHeaders();
+
+app.UseStaticFiles(); // other middleware
+app.UseAuthentication();
+app.UseRouting();
+
+app.UseAuthorization();
+
+app.MapGet("/", () => "Hello world");
+app.Run();
+```
+
+### 3. Apply custom policies to endpoints
+
+To apply a non-default policy to an endpoint, use the `WithSecurityHeadersPolicy(policy)` endpoint extension method, and pass in the name of the policy to apply:
+
+```csharp
+var builder = WebApplication.CreateBuilder();
+
+builder.Services.AddSecurityHeaderPolicies()
+ .SetDefaultPolicy(policy => policy.AddDefaultSecurityHeaders())
+ .AddPolicy("CustomPolicy", policy => policy.AddCustomHeader("X-Custom", "SomeValue"));
+
+var app = builder.Build();
+
+app.UseSecurityHeaders();
+
+app.UseStaticFiles();
+app.UseAuthentication();
+app.UseRouting();
+
+app.UseEndpointSecurityHeaders();
+app.UseAuthorization();
+
+app.MapGet("/", () => "Hello world")
+ .WithSecurityHeadersPolicy("CustomPolicy"); // 👈 Apply a named policy to the endpoint
+app.Run();
+```
+
+If you're using MVC controllers or Razor Pages, you can apply the `[SecurityHeadersPolicy(policyName)]` attribute to your endpoints:
+
+```csharp
+public class HomeController : ControllerBase
+{
+ [SecurityHeadersPolicy("CustomHeader")] // 👈 Apply a named policy to the endpoint
+ public IActionResult Index()
+ {
+ return View();
+ }
+}
+```
+
+Security headers are applied just before the response is sent. If you use the configuration described above, then the policy to apply is determined as follows, with the first applicable policy selected:
+
+1. If an endpoint has been selected, and a named policy is applied, use that.
+2. If a named or policy instance is passed to the `SecurityHeadersMiddleware()`, use that.
+3. If the default policy has been set using `SetDefaultPolicy()`, use that.
+4. Otherwise, apply the default headers (those added by `AddDefaultSecurityHeaders()`)
+
+## Customizing the headers per request
+
+If you need to use a different set of security headers for certain endpoints in your application, then configuring named policies [as described above](#applying-different-headers-to-different-endpoints) is the best approach.
+
+However, sometimes you need even more customization on a per-request basis. For example, perhaps you have a multi-tenant application, and you need to apply different headers based on a header in the request (or the response) that identifies the tenant. In this situation, you don't know at application startup which set of headers to apply.
+
+To customize the final `HeaderPolicyCollection` used for a request, you can use the `SetPolicySelector()` method available on `IServiceCollection.AddSecurityHeaderPolicies()`. This method take a `Func<>` argument which is passed a context object, and must return an `IReadOnlyHeaderPolicyCollection`. The `SetPolicySelector()` argument is invoked for every request, just before the final selected policy is applied, and allows you to change the `IReadOnlyHeaderPolicyCollection` to apply.
+
+The following code shows how to use a dependency injected service in combination with the `SetPolicySelector()` method. This isn't necessary, but rather shows that you can completely customise the applied headers in any way you need.
+
+```csharp
+var builder = WebApplication.CreateBuilder();
+
+// 👇 a custom service that selects the policy based on tenants
+builder.Services.AddScoped();
+builder.Services.AddSecurityHeaderPolicies()
+ .AddPolicy(policyName, p => p.AddCustomHeader("Custom-Header", "MyValue"))
+ .SetPolicySelector((PolicySelectorContext ctx) =>
+ {
+ // Use services from the DI container (if you need to)
+ IServiceProvider services = ctx.HttpContext.RequestServices;
+ var selector = services.GetService();
+ var tenant = services.GetService();
+ return selector.GetPolicy(tenant);
+ });
+```
+
+Note that you should avoid creating a `HeaderPolicyCollection` from scratch on each request. Instead, cache policies for multiple requests where possible. However, if you need to build a new policy based on the policy passed in the context object, you can create a mutable copy by calling `IReadOnlyHeaderPolicyCollection.Copy()`, adding/updating policies as required, and returning the `HeaderPolicyCollection`.
+
+## Applying different headers to JSON API endpoints
+
+As described in the [OWASP guidance](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers) about security headers, APIs that return only JSON do not require all the security headers added by `AddDefaultSecurityHeaders()`. If your API may return HTML or content other than JSON, it may be safest to use the default security headers nonetheless. If not, you can use the `AddDefaultApiSecurityHeaders()` method to apply a subset of headers. This method sets the following headers:
+
+* `X-Content-Type-Options: nosniff`
+* `Strict-Transport-Security: max-age=31536000; includeSubDomains` - _only applied to HTTPS responses_
+* `X-Frame-Options: Deny`
+* `Content-Security-Policy: default-src: none; frame-ancestors 'none'`
+* `Referrer-Policy: no-referrer`
+* `Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()`
+
+Apply it in the same way to your header policy collection:
+
+```csharp
+public void Configure(IApplicationBuilder app)
+{
+ var policyCollection = new HeaderPolicyCollection()
+ .AddApiDefaultSecurityHeaders();
+
+ app.UseSecurityHeaders(policyCollection);
+}
+```
+
+this is equivalent to
+
+```csharp
+public void Configure(IApplicationBuilder app)
+{
+ var policyCollection = new HeaderPolicyCollection()
+ .AddFrameOptionsDeny()
+ .AddContentTypeOptionsNoSniff()
+ .AddStrictTransportSecurityMaxAge()
+ .RemoveServerHeader()
+ .AddContentSecurityPolicy(builder =>
+ {
+ builder.AddDefaultSrc().None();
+ builder.AddFrameAncestors().None();
+ })
+ .AddReferrerPolicyNoReferrer()
+ .AddPermissionsPolicyWithDefaultSecureDirectives();
+
+ app.UseSecurityHeaders(policyCollection);
+}
+```
+
## RemoveServerHeader
One point to be aware of is that the `RemoveServerHeader` method will rarely (ever?) be sufficient to remove the `Server` header from your output. If any subsequent middleware in your application pipeline add the header, then this will be able to remove it. However Kestrel will generally add the `Server` header too late in the pipeline to be able to modify it.
@@ -168,7 +323,7 @@ In `Program.cs`, when constructing your app's `WebHostBuilder`, configure the `K
## AddContentSecurityPolicy
-The `Content-Security-Policy` (CSP) header is a very powerful header that can protect your website from a wide range of attacks. However, it's also totally possible to create a CSP header that completely breaks your app.
+The [`Content-Security-Policy` (CSP) header](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is a very powerful header that can protect your website from a wide range of attacks. However, it's also totally possible to create a CSP header that completely breaks your app.
The CSP has a dizzying array of options, only some of which are implemented in this project. Consequently, I highly recommend reading [this post by Scott Helme](https://scotthelme.co.uk/content-security-policy-an-introduction/), in which he discusses the impact of each "directive". I also highly recommend using the "report only" version of the header when you start. This won't break your site, but will report instances that it would be broken, by providing reports to a service such as report-uri.com.
@@ -185,7 +340,7 @@ public void Configure(IApplicationBuilder app)
}
```
-or by by passing `true` to the `AddContentSecurityPolicy` command
+or by passing `true` to the `AddContentSecurityPolicy` command
```csharp
public void Configure(IApplicationBuilder app)
@@ -282,7 +437,7 @@ For more information about the permissions, I recommend the following resources:
* MDN documentation: https://developer.mozilla.org/en-US/docs/Web/HTTP/Feature_Policy
* Google's introduction to Feature-Policy: https://developers.google.com/web/updates/2018/06/feature-policy
-You configure your CSP policy when you configure your `HeaderPolicyCollection` in `Startup.Configure`. For example:
+You configure your Permissions-Policy when you configure your `HeaderPolicyCollection` in `Startup.Configure`. For example:
```csharp
public void Configure(IApplicationBuilder app)
@@ -358,32 +513,61 @@ public void Configure(IApplicationBuilder app)
}
```
+If you want to lock down your permissions policy, you can use
+
+```csharp
+var policyCollection = new HeaderPolicyCollection()
+ .AddPermissionsPolicyWithDefaultSecureDirectives();
+```
+
+This applies a "secure" policy based on the [suggested by OWASP for APIs](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers):
+
+```HTTP
+Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()
+```
+
+Alternatively, if you want to relax some of these directives, you can use the builder version:
+
+```csharp
+var policyCollection = new HeaderPolicyCollection()
+ .AddPermissionsPolicy(builder =>
+ {
+ // add all the default versions
+ builder.AddDefaultSecureDirectives();
+
+ // override a directive
+ builder.AddGeolocation().Self(); // geolocation 'self'
+ });
+```
+
## Using Nonces and generated-hashes with Content-Security-Policy
-The use of a secure Content-Security-Policy can sometimes be problematic when you need to include inline-scripts, styles, or other objects that haven't been whitelisted. You can achieve this in two ways - using a "nonce" (or "number-used-once"), or specifying the hash of the content to include.
+The use of a secure Content-Security-Policy can sometimes be problematic when you need to include inline-scripts, styles, or other objects that haven't been allow-listed. You can achieve this in two ways - using a "nonce" (or "number-used-once"), or specifying the hash of the content to include.
-To help with this you can install the NetEscapades.AspNetCore.SecurityHeaders.TagHelpers package, which provides helpers for generating a nonce per request, which is attached to the HTML element, and included in the CSP header. A similar method helper exists for `
-
-
-
-
-
-
-
-
This application consists of:
-
- - Sample pages using ASP.NET Core MVC
- - Gulp and Bower for managing client-side libraries
- - Theming using Bootstrap
-
-
-
-
-
-
-
-
-
-
-