WIP separation Web et API

This commit is contained in:
Paul Schneider
2025-02-12 20:41:14 +00:00
parent 6cd5f1d041
commit c2ae054719
55 changed files with 71 additions and 98 deletions

View File

@ -5,6 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" />
<ProjectReference Include="../Yavsc/Yavsc.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -30,7 +30,7 @@ namespace Yavsc.WebApi.Controllers
UserManager = userManager; UserManager = userManager;
this.roleManager = roleManager; this.roleManager = roleManager;
_signInManager = signInManager; _signInManager = signInManager;
_logger = loggerFactory.CreateLogger("ApiAuth"); _logger = loggerFactory.CreateLogger(nameof(ApiAccountController));
_dbContext = dbContext; _dbContext = dbContext;
} }
@ -154,7 +154,7 @@ namespace Yavsc.WebApi.Controllers
return Ok(user); return Ok(user);
} }
[HttpGet("~/api/myhost")] [HttpGet("myhost")]
public IActionResult MyHost () public IActionResult MyHost ()
{ {
return Ok(new { host = Request.ForHost() }); return Ok(new { host = Request.ForHost() });
@ -165,7 +165,7 @@ namespace Yavsc.WebApi.Controllers
/// </summary> /// </summary>
/// <param name="me">MyUpdate containing the new user name </param> /// <param name="me">MyUpdate containing the new user name </param>
/// <returns>Ok when all is ok.</returns> /// <returns>Ok when all is ok.</returns>
[HttpPut("~/api/me")] [HttpPut("me")]
public async Task<IActionResult> UpdateMe(UserInfo me) public async Task<IActionResult> UpdateMe(UserInfo me)
{ {
if (!ModelState.IsValid) return new BadRequestObjectResult( if (!ModelState.IsValid) return new BadRequestObjectResult(

View File

@ -44,7 +44,7 @@ internal class Program
.AllowAnyMethod(); .AllowAnyMethod();
}); });
}) })
.AddControllers(); .AddControllersWithViews();
// accepts any access token issued by identity server // accepts any access token issued by identity server
var authenticationBuilder = services.AddAuthentication() var authenticationBuilder = services.AddAuthentication()
@ -64,14 +64,22 @@ internal class Program
app app
.UseRouting() .UseRouting()
.UseAuthentication() .UseAuthentication()
.UseAuthorization() .UseAuthorization().UseCors("default")
.UseCors("default"); .UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute()
.RequireAuthorization();
});
app.MapGet("/identity", (HttpContext context) => app.MapGet("/identity", (HttpContext context) =>
new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value })) new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value }))
).RequireAuthorization("ApiScope"); ).RequireAuthorization("ApiScope");
await app.RunAsync(); await app.RunAsync();
} };
} }
} }

View File

@ -341,19 +341,13 @@ internal static class HostingExtensions
}) })
.AddCors(options => .AddCors(options =>
{ {
options.AddPolicy("CorsPolicy", builder => options.AddPolicy("default", builder =>
{ {
_ = builder.WithOrigins("*") _ = builder.WithOrigins("*")
.AllowAnyHeader() .AllowAnyHeader()
.AllowAnyMethod(); .AllowAnyMethod();
}); });
options.AddPolicy("default", policy =>
{
policy.WithOrigins("https://localhost:5003")
.AllowAnyHeader()
.AllowAnyMethod();
});
}); });
services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
@ -379,6 +373,7 @@ internal static class HostingExtensions
app.UseRouting(); app.UseRouting();
app.UseIdentityServer(); app.UseIdentityServer();
app.UseAuthorization(); app.UseAuthorization();
app.UseCors("default");
app.MapControllerRoute( app.MapControllerRoute(
name: "default", name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"); pattern: "{controller=Home}/{action=Index}/{id?}");

View File

@ -17,20 +17,20 @@
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" /> <PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.12"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.13">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.12"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.13">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.12" IncludeAssets="All" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.13" />
<PackageReference Include="Google.Apis.Compute.v1" Version="1.54.0" /> <PackageReference Include="Google.Apis.Compute.v1" Version="1.54.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.2.0" />

View File

@ -12,13 +12,13 @@ using Microsoft.AspNetCore.Mvc;
// But, this redirect URI doesn't need to match the OAuth parameter, it's serialized in the query state, // But, this redirect URI doesn't need to match the OAuth parameter, it's serialized in the query state,
// to be used once the identification ends. // to be used once the identification ends.
var properties = new AuthenticationProperties { RedirectUri = returnUrl }; var properties = new AuthenticationProperties { RedirectUri = returnUrl };
return new ChallengeResult("Yavsc", properties); return new ChallengeResult("oidc", properties);
} }
[HttpGet("~/signout")] [HttpGet("~/signout")]
public async Task<IActionResult> SignOut(string returnUrl="/") { public async Task<IActionResult> SignOut(string returnUrl="/") {
return SignOut("Cookies", "Yavsc"); return SignOut("Cookies", "oidc");
} }
} }

View File

@ -57,7 +57,7 @@ namespace testOauthClient.Controllers
var client = new HttpClient(new HttpClientHandler(){ AllowAutoRedirect=false }); var client = new HttpClient(new HttpClientHandler(){ AllowAutoRedirect=false });
client.DefaultRequestHeaders.Add("Accept", "application/json"); client.DefaultRequestHeaders.Add("Accept", "application/json");
client.SetBearerToken(accessToken); client.SetBearerToken(accessToken);
var content = await client.GetAsync("https://localhost:5001/api/account/me"); var content = await client.GetAsync("https://localhost:6001/api/account/me");
content.EnsureSuccessStatusCode(); content.EnsureSuccessStatusCode();
var json = await content.Content.ReadAsStreamAsync(); var json = await content.Content.ReadAsStreamAsync();
var obj = JsonSerializer.Deserialize<JsonElement>(json); var obj = JsonSerializer.Deserialize<JsonElement>(json);

View File

@ -4,25 +4,17 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:5002", "applicationUrl": "https://localhost:5003",
"sslPort": 5003 "sslPort": 5003
} }
}, },
"profiles": { "profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5002",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": { "https": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"applicationUrl": "https://localhost:5003;http://localhost:5002", "applicationUrl": "https://localhost:5003;",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }

View File

@ -10,43 +10,43 @@ public class Startup
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddControllersWithViews(); services.AddControllersWithViews();
services
JwtSecurityTokenHandler.DefaultMapInboundClaims = false; .AddAuthentication(options =>
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "Yavsc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("Yavsc", options =>
{
options.Authority = "https://localhost:5001";
options.ClientId = "mvc";
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
options.ResponseType = "code";
options.UsePkce = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("offline_access");
options.Scope.Add("scope2");
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.ClaimActions.MapUniqueJsonKey("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
options.ClaimActions.MapUniqueJsonKey("role", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
options.ClaimActions.MapUniqueJsonKey("roles", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
options.TokenValidationParameters = new TokenValidationParameters
{ {
NameClaimType = "name", options.DefaultScheme = "Cookies";
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" options.DefaultChallengeScheme = "oidc";
}; })
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:5001";
options.ClientId = "mvc";
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
options.ResponseType = "code";
options.Scope.Add("scope2");
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.ClaimActions.MapUniqueJsonKey(
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
options.ClaimActions.MapUniqueJsonKey("role",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
options.ClaimActions.MapUniqueJsonKey("roles",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
};
});
});
} }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

View File

@ -9,9 +9,6 @@
"Kestrel": { "Kestrel": {
"Endpoints": "Endpoints":
{ {
"Http": {
"Url": "http://localhost:5002"
},
"Https": { "Https": {
"Url": "https://localhost:5003" "Url": "https://localhost:5003"
} }

View File

@ -2,7 +2,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" /> <ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" />
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" /> <PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -79,25 +79,5 @@ namespace yavscTests
} }
} }
public static IEnumerable<object[]> GetLoginIntentData(int numTests)
{
var allData = new List<object[]>();
allData.Add(new object[] { "blouh", "profile",
"http://localhost:5000/authorize", "http://localhost:5000/oauth/success",
"http://localhost:5000/token", "http://localhost:5000/authorize"});
allData.Add(new object[] { "blouh", "profile",
"http://localhost:5000/authorize", "http://localhost:5000/oauth/success",
"http://localhost:5000/token", "http://localhost:5000/authorize"});
return allData.Take(numTests);;
}
} }
} }