Version: 1.0

This commit is contained in:
Karsten Jeppesen
2025-11-21 08:34:04 +01:00
parent 6567700880
commit fee1f04bbc
251 changed files with 153893 additions and 0 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,12 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\Master20251026\\dst\\WebApplication-API\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}

View File

@@ -0,0 +1,12 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\Master20251026\\dst\\WebApplication-API\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}

View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36616.10 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApplication-API", "WebApplication-API\WebApplication-API.csproj", "{CCE0BD23-703B-4CDC-958C-DDE09FB70D00}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CCE0BD23-703B-4CDC-958C-DDE09FB70D00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCE0BD23-703B-4CDC-958C-DDE09FB70D00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCE0BD23-703B-4CDC-958C-DDE09FB70D00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCE0BD23-703B-4CDC-958C-DDE09FB70D00}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {307B341F-18AE-4D3A-AC0D-C2C5EE035FA5}
EndGlobalSection
EndGlobal

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,14 @@
{
"author": "Karsten Jeppesen kaje@ucn.dk",
"classifications": [ "Web", "Web API" ],
"name": "OpenIDConnectAPI", // Name of the template
"defaultName": "OpenID-API", // Suggested default project name
"identity": "UCNkaje.OpenIDConnectAPI", // Globally unique name for this template
"shortName": "oidc_api", // Short name that can be used on the cli
"tags": {
"language": "C#", // Specify that this template is in C#.
"type": "project" // and it is a project typr
},
"sourceName": "WebApplication-API", // Will replace this string with the value provided via -n.
"preferNameDirectory" : "true" // So mlikely always the parent folder name = project name
}

View File

@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication_API.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
// Authorization is shown here using the Authorize policy option
//[Authorize]
[Authorize(Policy = "Admin")]
[HttpGet(Name = "WeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}

View File

@@ -0,0 +1,190 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.HttpOverrides;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.OpenApi.Models;
// Required NuGet Packages
// Microsoft.AspNetCore.Authentication.JwtBearer
// Microsoft.AspNetCore.Authentication.OpenIdConnect
// Microsoft.IdentityModel.Protocols.OpenIdConnect
// Microsoft.AspNet.WebApi.Core
namespace WebApplication_API.OpenIDConnect
{
public class OpenIDConnectUtils
{
// Configuration priority from highest to lowest
// Highest: Command line arguments
// : Non-prefixed environment variables
// : User secrets from the .NET User Secrets Manager
// : Any appsettings.{ ENVIRONMENT_NAME }.json files
// : The appsettings.json file
// Lowest : Fallback to the host configuration
/// <summary>
/// Setting up OpenIDConnect authentication (Program.cs)
/// </summary>
/// <param name="builder">WebApplicationBuilder</param>
public void ConfigureBuild(WebApplicationBuilder builder)
{
MyConfiguration.Set(builder.Configuration);
builder.Services.AddControllers();
// >>> This adds the authentication service
builder.Services
.AddAuthentication()
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
// OpenIDRealmURI coud be "https://auth.a.ucnit.eu/realms/xOIDCx"
options.Authority = builder.Configuration["OpenIDRealmURI"];
options.Audience = "account";
options.MapInboundClaims = false;
options.Events = new JwtBearerEvents()
{
OnMessageReceived = msg =>
{
var token = msg?.Request.Headers.Authorization.ToString();
string path = msg?.Request.Path ?? "";
if (!string.IsNullOrEmpty(token))
{
Console.WriteLine("Access token");
Console.WriteLine($"URL: {path}");
Console.WriteLine($"Token: {token}\r\n");
}
else
{
Console.WriteLine("Access token");
Console.WriteLine("URL: " + path);
Console.WriteLine("Token: No access token provided\r\n");
}
return Task.CompletedTask;
}
};
options.Events = new JwtBearerEvents()
{
//...
OnTokenValidated = ctx =>
{
Console.WriteLine();
Console.WriteLine("Claims from the access token");
if (ctx?.Principal != null)
{
foreach (var claim in ctx.Principal.Claims)
{
Console.WriteLine($"{claim.Type} - {claim.Value}");
}
}
Console.WriteLine();
return Task.CompletedTask;
}
};
});
// <<<
// >>> This adds the authorization service
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => policy.RequireClaim("roles", "admin", "warlock"));
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
// >>> Add Swagger with Authorization option
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "My API",
Version = "v1"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
// <<<
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
}
/// <summary>
/// Map synthetic endpoints for authentication
/// </summary>
/// <param name="app">WebApplication</param>
public void ConfigureApp(WebApplication app)
{
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
}
public JwtSecurityToken GetJwtPayload(HttpContext myContext)
{
var handler = new JwtSecurityTokenHandler();
return handler.ReadJwtToken(myContext.GetTokenAsync("access_token").Result);
}
public string GetJwtClaim(HttpContext myContext, string theClaim)
{
JwtSecurityToken jwtPayload = GetJwtPayload(myContext);
return jwtPayload.Claims.FirstOrDefault(claim => claim.Type == theClaim).Value;
}
}
static public class MyConfiguration
{
static ConfigurationManager _config;
static public void Set(ConfigurationManager config)
{
_config = config;
}
static public ConfigurationManager Get()
{
return _config;
}
}
}

View File

@@ -0,0 +1,14 @@
using WebApplication_API.OpenIDConnect;
var builder = WebApplication.CreateBuilder(args);
// Configure for OpenID
OpenIDConnectUtils oidcConfig = new();
oidcConfig.ConfigureBuild(builder);
var app = builder.Build();
// Configure for OpenID
oidcConfig.ConfigureApp(app);
app.Run();

View File

@@ -0,0 +1,41 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5124"
},
"https": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:8888;http://localhost:5124"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:21570",
"sslPort": 44323
}
}
}

View File

@@ -0,0 +1,13 @@
namespace WebApplication_API
{
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>WebApplication_API</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.21" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.21" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.14.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,6 @@
@WebApplication_API_HostAddress = http://localhost:5124
GET {{WebApplication_API_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,11 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Comment3": "Arguments for KeyCloak Code Flow",
"OpenIDRealmURI": "https://auth.a.ucnit.eu/realms/xOIDCx"
}

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("WebApplication-API")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("WebApplication-API")]
[assembly: System.Reflection.AssemblyTitleAttribute("WebApplication-API")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class.

View File

@@ -0,0 +1 @@
80622a8f559dda718649f0f3556b4c52b2748d871a8571cda9d6a89605f77cb2

View File

@@ -0,0 +1,21 @@
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb = true
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = WebApplication_API
build_property.RootNamespace = WebApplication_API
build_property.ProjectDir = D:\Master20251026\dst\WebApplication-API\WebApplication-API\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.RazorLangVersion = 8.0
build_property.SupportLocalizedComponentNames =
build_property.GenerateRazorMetadataSourceChecksumAttributes =
build_property.MSBuildProjectDirectory = D:\Master20251026\dst\WebApplication-API\WebApplication-API
build_property._RazorSourceGeneratorDebug =
build_property.EffectiveAnalysisLevelStyle = 8.0
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,17 @@
// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@@ -0,0 +1 @@
{"GlobalPropertiesHash":"VuaE3syYJ2cNgABg1DseBHbk2+NDr8cEi5lWP7zbvgQ=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["qf6EYwVoVtzr6j4VvwpAB/MXJs4kQ1HlqkeEu\u002BE/MQU=","uzr/D\u002BYXZ9duD8p458pp9hf1xT6SHYMse4TWSBokfe4="],"CachedAssets":{},"CachedCopyCandidates":{}}