Added new web api project for testing; AcceptTokenFromQuery feature in NDB.Security.Authentication.Identity

messaging
Tudor Stanciu 2021-11-24 03:40:30 +02:00
parent 32d8dc0c4e
commit 25ea2cc4f1
13 changed files with 280 additions and 21 deletions

View File

@ -8,5 +8,6 @@ namespace NDB.Security.Authentication.Identity.Abstractions
Func<HttpRequest, bool> AuthenticateAsGuest { get; }
int GuestUserId { get; }
string GuestUserName { get; }
bool AcceptTokenFromQuery { get; }
}
}

View File

@ -10,7 +10,7 @@ namespace NDB.Security.Authentication.Identity
{
public static IServiceCollection AddIdentityAuthentication(this IServiceCollection services, string identityServerBaseAddress)
{
services.AddIdentityAuthentication(identityServerBaseAddress, new Services.AuthenticationOptions());
services.AddIdentityAuthentication(identityServerBaseAddress, new Models.AuthenticationOptions());
return services;
}

View File

@ -0,0 +1,8 @@
namespace NDB.Security.Authentication.Identity.Constants
{
internal struct QueryParams
{
public const string
Token = "token";
}
}

View File

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NDB.Security.Authentication.Identity.Abstractions;
using c = NDB.Security.Authentication.Identity.Constants;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
@ -27,28 +28,17 @@ namespace NDB.Security.Authentication.Identity
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
var token = GetAuthorizationToken();
if (token != null)
{
var authenticateAsGuest = _authenticationOptions.AuthenticateAsGuest?.Invoke(Request) ?? false;
if (authenticateAsGuest)
{
var guestTicket = GetGuestAuthenticationTicket(_authenticationOptions.GuestUserId, _authenticationOptions.GuestUserName);
return AuthenticateResult.Success(guestTicket);
}
return AuthenticateResult.Fail("Missing Authorization Header");
}
TokenCore tokenCore;
try
{
var authorizationHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var token = authorizationHeader.Parameter;
tokenCore = await _identityService.Authorize(token);
}
catch
{
return AuthenticateResult.Fail("Invalid authorization header");
return AuthenticateResult.Fail("Invalid authorization");
}
if (tokenCore == null)
@ -58,6 +48,36 @@ namespace NDB.Security.Authentication.Identity
return AuthenticateResult.Success(ticket);
}
var authenticateAsGuest = _authenticationOptions.AuthenticateAsGuest?.Invoke(Request) ?? false;
if (authenticateAsGuest)
{
var guestTicket = GetGuestAuthenticationTicket(_authenticationOptions.GuestUserId, _authenticationOptions.GuestUserName);
return AuthenticateResult.Success(guestTicket);
}
return AuthenticateResult.Fail("Missing authorization header");
}
private string GetAuthorizationToken()
{
if (Request.Headers.ContainsKey("Authorization"))
{
var authorizationHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var token = authorizationHeader.Parameter;
return token;
}
if (_authenticationOptions.AcceptTokenFromQuery
&& Request.Query.Count > 0
&& Request.Query.ContainsKey(c.QueryParams.Token))
{
var token = Request.Query[c.QueryParams.Token];
return token.ToString();
}
return null;
}
private AuthenticationTicket GetGuestAuthenticationTicket(int guestId, string guestName)
{
var claims = new[] {

View File

@ -2,7 +2,7 @@
using NDB.Security.Authentication.Identity.Abstractions;
using System;
namespace NDB.Security.Authentication.Identity.Services
namespace NDB.Security.Authentication.Identity.Models
{
public class AuthenticationOptions : IAuthenticationOptions
{
@ -11,5 +11,6 @@ namespace NDB.Security.Authentication.Identity.Services
public int GuestUserId { get; set; }
public string GuestUserName { get; set; }
public bool AcceptTokenFromQuery { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
namespace NDB.Test.Api.Controllers
{
[Authorize]
[ApiController]
[Route("[controller]")]
public class DebugController : ControllerBase
{
private readonly ILogger<DebugController> _logger;
public DebugController(ILogger<DebugController> logger)
{
_logger = logger;
}
[AllowAnonymous]
[HttpGet("ping")]
public IActionResult Ping()
{
return Ok($"Ping success. System datetime: {DateTime.Now}");
}
[HttpGet("ping-auth")]
public IActionResult PingAuth()
{
return Ok($"Secured ping success. System datetime: {DateTime.Now}");
}
}
}

View File

@ -0,0 +1,61 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using NDB.Security.Authentication.Identity;
using NDB.Security.Authentication.Identity.Models;
using System.Linq;
namespace NDB.Test.Api.Extensions
{
public static class AuthenticationExtensions
{
private record GuestRoute(string Route, int PathElements);
private static readonly GuestRoute[] _resourcesRoutes = new GuestRoute[]
{
new GuestRoute("/resources", 1),
new GuestRoute("/images", 1),
new GuestRoute("/spot", 2)
};
public static IServiceCollection AddIdentityAuthentication(this IServiceCollection services, string identityServerBaseAddress)
{
var authenticationOptions = new AuthenticationOptions()
{
AuthenticateAsGuest = (HttpRequest request) =>
{
var authenticateAsGuest = AuthenticateAsGuest(request);
return authenticateAsGuest;
},
GuestUserName = "Guest",
GuestUserId = -111,
AcceptTokenFromQuery = true
};
services.AddIdentityAuthentication(identityServerBaseAddress, authenticationOptions);
return services;
}
private static bool AuthenticateAsGuest(HttpRequest request)
{
if (!request.Path.HasValue)
return false;
var guestRoute = _resourcesRoutes.FirstOrDefault(z => request.Path.Value.StartsWith(z.Route));
if (guestRoute == null)
return false;
var resourceRequestedById = request.Query.Count > 0 && request.Query.ContainsKey("id");
if (resourceRequestedById)
return false;
var resourceRequestedByCode = request.Query.Count > 0 && request.Query.ContainsKey("code");
if (resourceRequestedByCode)
return true;
var resourceRequestedByName = request.Path.Value.Replace(guestRoute.Route, string.Empty).Substring(1).Split("/").Length == guestRoute.PathElements;
if (resourceRequestedByName)
return true;
return false;
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NDB.Extensions.Swagger\NDB.Extensions.Swagger.csproj" />
<ProjectReference Include="..\NDB.Security.Authentication.Identity\NDB.Security.Authentication.Identity.csproj" />
</ItemGroup>
</Project>

26
NDB.Test.Api/Program.cs Normal file
View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NDB.Test.Api
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@ -0,0 +1,15 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"NDB.Test.Api": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

54
NDB.Test.Api/Startup.cs Normal file
View File

@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using NDB.Extensions.Swagger;
using NDB.Extensions.Swagger.Constants;
using NDB.Test.Api.Extensions;
namespace NDB.Test.Api
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add basic authentication
services.AddIdentityAuthentication(Configuration.GetSection("IdentityServer")["BaseAddress"]);
services.AddControllers();
services.AddSwagger("NDB.Test.Api", AuthorizationType.InhouseIdentity);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NDB.Test.Api v1"));
}
app.ConfigureSwagger("NDB.Test.Api v1");
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

View File

@ -0,0 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"IdentityServer": {
//"BaseAddress": "http://localhost:5063/"
"BaseAddress": "https://toodle.ddns.net/identity-server-api/"
}
}

16
NDB.sln
View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29324.140
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NDB.Hosting.WindowsService", "NDB.Hosting.WindowsService\NDB.Hosting.WindowsService.csproj", "{F4AB0B71-6B14-402B-8B05-128AFB3E06D9}"
EndProject
@ -54,7 +54,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NDB.Security.Authentication
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "caching", "caching", "{A206A484-3ACF-4032-8B36-AC93A72B0B88}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDB.Extensions.Caching", "NDB.Extensions.Caching\NDB.Extensions.Caching.csproj", "{3E045EE6-A290-467C-B503-3A6CB0065C97}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NDB.Extensions.Caching", "NDB.Extensions.Caching\NDB.Extensions.Caching.csproj", "{3E045EE6-A290-467C-B503-3A6CB0065C97}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{CCEE458E-02A8-42FD-8F5F-A35481A23303}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDB.Test.Api", "NDB.Test.Api\NDB.Test.Api.csproj", "{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -102,6 +106,10 @@ Global
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E045EE6-A290-467C-B503-3A6CB0065C97}.Release|Any CPU.Build.0 = Release|Any CPU
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -127,6 +135,8 @@ Global
{5C0637C8-6BA4-4EAE-97CA-BB8D98B2991A} = {B8132F39-6677-4D70-84CA-9747DC9086B3}
{A206A484-3ACF-4032-8B36-AC93A72B0B88} = {B50B55F0-9E6E-4061-9100-E2329A44E76B}
{3E045EE6-A290-467C-B503-3A6CB0065C97} = {A206A484-3ACF-4032-8B36-AC93A72B0B88}
{CCEE458E-02A8-42FD-8F5F-A35481A23303} = {E0202271-4E92-4DB8-900D-B5FD745B9278}
{F717BE3D-F5F4-4D99-B96D-D0A23E8BED01} = {CCEE458E-02A8-42FD-8F5F-A35481A23303}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87541BAB-3FAC-4ADB-A7FB-8228DA87843D}