Skip to content

Commit

Permalink
Small updates
Browse files Browse the repository at this point in the history
  • Loading branch information
damienbod committed Jan 2, 2025
1 parent 958b0e2 commit 7560d1b
Show file tree
Hide file tree
Showing 16 changed files with 513 additions and 331 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace ServiceApi.Controllers;

[Authorize(Policy = "ValidateAccessTokenPolicy", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("[controller]")]
public class ApiForServiceDataController : ControllerBase
{
[HttpGet]
public IEnumerable<string> Get()
{
return new List<string> { "app-app Service API data 1", "service API data 2" };
}
}
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Collections.Generic;

namespace ServiceApi.Controllers;

[Authorize(Policy = "ValidateAccessTokenPolicy", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("[controller]")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[Produces("application/json")]
[SwaggerTag("Service API for demo service data")]
public class ApiForServiceDataController : ControllerBase
{
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<string>))]
[SwaggerOperation(OperationId = "Get", Summary = "Gets service data")]
public IEnumerable<string> Get()
{
return new List<string> { "app-app Service API data 1", "service API data 2" };
}
}
10 changes: 10 additions & 0 deletions BlazorWithApis/ServiceApi/Policies/HasServiceApiRoleHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace ServiceApi;

Expand All @@ -14,6 +18,12 @@ protected override Task HandleRequirementAsync(AuthorizationHandlerContext conte

var roleClaims = context.User.Claims.Where(t => t.Type == "roles");

// MS namespace: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
if (!roleClaims.Any())
{
roleClaims = context.User.Claims.Where(t => t.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
}

if (roleClaims != null && HasServiceApiRole(roleClaims))
{
context.Succeed(requirement);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Authorization;

namespace ServiceApi;

using Microsoft.AspNetCore.Authorization;

namespace ServiceApi;

public class HasServiceApiRoleRequirement : IAuthorizationRequirement { }
126 changes: 34 additions & 92 deletions BlazorWithApis/ServiceApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,92 +1,34 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Logging;
using Microsoft.OpenApi.Models;
using ServiceApi;
using System.IdentityModel.Tokens.Jwt;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IAuthorizationHandler, HasServiceApiRoleHandler>();
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);
builder.Services.AddControllers();

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ValidateAccessTokenPolicy", validateAccessTokenPolicy =>
{
validateAccessTokenPolicy.Requirements.Add(new HasServiceApiRoleRequirement());

// Validate id of application for which the token was created
// In this case the UI application
validateAccessTokenPolicy.RequireClaim("azp", "2b50a014-f353-4c10-aace-024f19a55569");

// only allow tokens which used "Private key JWT Client authentication"
// // https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens
// Indicates how the client was authenticated. For a public client, the value is "0".
// If client ID and client secret are used, the value is "1".
// If a client certificate was used for authentication, the value is "2".
validateAccessTokenPolicy.RequireClaim("azpacr", "1");
});
});

builder.Services.AddSwaggerGen(c =>
{
// add JWT Authentication
var securityScheme = new OpenApiSecurityScheme
{
Name = "JWT Authentication",
Description = "Enter JWT Bearer token **_only_**",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer", // must be lower case
BearerFormat = "JWT",
Reference = new OpenApiReference
{
Id = JwtBearerDefaults.AuthenticationScheme,
Type = ReferenceType.SecurityScheme
}
};
c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{securityScheme, Array.Empty<string>()}
});

c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Service API One",
Version = "v1",
Description = "User API One",
Contact = new OpenApiContact
{
Name = "damienbod",
Email = string.Empty,
Url = new Uri("https://damienbod.com/"),
},
});
});
var app = builder.Build();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
IdentityModelEventSource.ShowPII = true;
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Service API One");
c.RoutePrefix = string.Empty;
});

app.UseHttpsRedirection();

app.UseRouting();

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

app.MapControllers();

app.Run();
using Microsoft.AspNetCore.Builder;
using Serilog;
using ServiceApi;
using System;

Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();

Log.Information("Starting ServiceApi application");

try
{
var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((context, loggerConfiguration) => loggerConfiguration
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}")
.ReadFrom.Configuration(context.Configuration));

var app = builder
.ConfigureServices()
.ConfigurePipeline();

app.Run();
}
catch (Exception ex) when (ex.GetType().Name is not "StopTheHostException" && ex.GetType().Name is not "HostAbortedException")
{
Log.Fatal(ex, "Unhandled exception");
}
finally
{
Log.Information("Shut down complete");
Log.CloseAndFlush();
}
24 changes: 12 additions & 12 deletions BlazorWithApis/ServiceApi/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"profiles": {
"ServiceApi": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:44324"
}
}
{
"profiles": {
"ServiceApi": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:44324"
}
}
}
57 changes: 57 additions & 0 deletions BlazorWithApis/ServiceApi/SecurityHeadersDefinitions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Microsoft.AspNetCore.Builder;

namespace ServiceApi;

public static class SecurityHeadersDefinitions
{
public static HeaderPolicyCollection GetHeaderPolicyCollection(bool isDev)
{
var policy = new HeaderPolicyCollection()
.AddFrameOptionsDeny()
.AddContentTypeOptionsNoSniff()
.AddReferrerPolicyStrictOriginWhenCrossOrigin()
.RemoveServerHeader()
.AddCrossOriginOpenerPolicy(builder => builder.SameOrigin())
.AddCrossOriginEmbedderPolicy(builder => builder.RequireCorp())
.AddCrossOriginResourcePolicy(builder => builder.SameOrigin())
.AddContentSecurityPolicy(builder =>
{
builder.AddObjectSrc().None();
builder.AddBlockAllMixedContent();
builder.AddImgSrc().Self().From("data:");
builder.AddFormAction().Self();
builder.AddFontSrc().Self();
builder.AddStyleSrc().Self();
builder.AddBaseUri().Self();
builder.AddScriptSrc().WithNonce();
builder.AddFrameAncestors().None();
})
.AddPermissionsPolicy(builder =>
{
builder.AddAccelerometer().None();
builder.AddAutoplay().None();
builder.AddCamera().None();
builder.AddEncryptedMedia().None();
builder.AddFullscreen().All();
builder.AddGeolocation().None();
builder.AddGyroscope().None();
builder.AddMagnetometer().None();
builder.AddMicrophone().None();
builder.AddMidi().None();
builder.AddPayment().None();
builder.AddPictureInPicture().None();
builder.AddSyncXHR().None();
builder.AddUsb().None();
});

if (!isDev)
{
// maxage = one year in seconds
policy.AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 60 * 60 * 24 * 365);
}

policy.ApplyDocumentHeadersToAllResponses();

return policy;
}
}
43 changes: 28 additions & 15 deletions BlazorWithApis/ServiceApi/ServiceApi.csproj
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<UserSecretsId>196b270c-b0c0-4b90-8f04-d3108e701d51</UserSecretsId>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Identity.Web" Version="2.17.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

</Project>
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>196b270c-b0c0-4b90-8f04-d3108e701d51</UserSecretsId>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Identity.Web" Version="3.5.0" />

<PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.24.0" />
<PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders.TagHelpers" Version="0.24.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="7.2.0" />

<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />

<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageReference Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
</ItemGroup>

</Project>
Loading

0 comments on commit 7560d1b

Please sign in to comment.